From 7e8d8308e35df91d2f1afdf94c93a2d14f3e5b16 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 3 May 2008 14:37:10 +0000 Subject: [PATCH 001/294] Initial Import --- client/about.ui | 88 + client/hexlineedit.cpp | 69 + client/hexlineedit.h | 24 + client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 11 + client/mainwindow.cpp | 30 + client/mainwindow.h | 18 + client/mainwindow.ui | 159 ++ client/modeltest.cpp | 542 +++++ client/modeltest.h | 76 + client/modeltest.pri | 4 + client/mythread.cpp | 24 + client/mythread.h | 23 + client/ostinato.pro | 42 + client/ostinato.qrc | 21 + client/port.cpp | 24 + client/port.h | 55 + client/portgroup.cpp | 169 ++ client/portgroup.h | 66 + client/portgrouplist.cpp | 108 + client/portgrouplist.h | 47 + client/portgrouplistmodel.h | 55 + client/portlistmodel.cpp | 191 ++ client/portlistmodel.h | 37 + client/portmodel.cpp | 314 +++ client/portmodel.h | 51 + client/portstatsfilter.ui | 124 + client/portstatsfilterdialog.cpp | 9 + client/portstatsfilterdialog.h | 19 + client/portstatsmodel.cpp | 119 + client/portstatsmodel.h | 59 + client/portstatswindow.cpp | 15 + client/portstatswindow.h | 21 + client/portstatswindow.ui | 133 + client/portswindow.cpp | 320 +++ client/portswindow.h | 56 + client/portswindow.ui | 214 ++ client/stream.cpp | 123 + client/stream.h | 307 +++ client/streamconfigdialog.cpp | 677 ++++++ client/streamconfigdialog.h | 58 + client/streamconfigdialog.ui | 3224 +++++++++++++++++++++++++ client/streamlistmodel.cpp | 119 + client/streamlistmodel.h | 46 + client/streammodel.cpp | 218 ++ client/streammodel.h | 49 + common/protocol.h | 53 + server/abstracthost.h | 12 + server/drone.cpp | 85 + server/drone.h | 36 + server/drone.pro | 9 + server/drone.ui | 126 + server/drone_main.cpp | 22 + server/rxtx.cpp | 147 ++ server/rxtx.h | 35 + 72 files changed, 8683 insertions(+) create mode 100644 client/about.ui create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/mythread.cpp create mode 100644 client/mythread.h create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portgrouplistmodel.h create mode 100644 client/portlistmodel.cpp create mode 100644 client/portlistmodel.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistmodel.cpp create mode 100644 client/streamlistmodel.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/protocol.h create mode 100644 server/abstracthost.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone.ui create mode 100644 server/drone_main.cpp create mode 100644 server/rxtx.cpp create mode 100644 server/rxtx.h diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..2256d60 --- /dev/null +++ b/client/about.ui @@ -0,0 +1,88 @@ + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + QFrame::Box + + + QFrame::Raised + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..a983bfd --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,69 @@ +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets); +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent( QFocusEvent *e ) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else + bool isOk; + ulong num; + QString str; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, str, 4)); + qDebug("after = %s\n", text().toAscii().data()); + qDebug("after2 = %s\n", str.toAscii().data()); +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..7c5811e --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,24 @@ +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/bullet_error.png b/client/icons/bullet_error.png new file mode 100644 index 0000000000000000000000000000000000000000..bca2b491fd4a72918c37eb218bf9655d7f514750 GIT binary patch literal 454 zcmV;%0XhDOP);<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/magnifier.png b/client/icons/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3d97f75e9cde9c143980d89272fe61fc2d64ee GIT binary patch literal 615 zcmV-t0+{`YP)gNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3WdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wHVI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + MainWindow mainWin; + + mainWin.show(); + return app.exec(); +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..7eef2f3 --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,30 @@ +#include +#include "mainwindow.h" +#include "portswindow.h" +#include "portstatswindow.h" +#include "portgrouplist.h" + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + pgl = new PortGroupList; + + PortsWindow *portsWindow = new PortsWindow(pgl, this); + PortStatsWindow *statsWindow = new PortStatsWindow(pgl, this); + QDockWidget *dock = new QDockWidget(tr("Ports"), this); + QDockWidget *dock2 = new QDockWidget(tr("Stats"), this); + + setupUi(this); + + dock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, dock); + dock2->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, dock2); +} + +void MainWindow::on_actionPreferences_triggered() +{ +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..da412ec --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,18 @@ +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include +#include "ui_mainwindow.h" + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget *parent = 0); + +public slots: + void on_actionPreferences_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..5d43edb --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,159 @@ + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 21 + + + + + File + + + + + + View + + + + + + Window + + + + + + + + + + + + Help + + + + + + + + + + + + Open Config + + + + + Save Config + + + + + Save Config As ... + + + + + Open Capture + + + + + Save Capture + + + + + Save Capture As ... + + + + + E&xit + + + + + Copy Port Config + + + + + Paste Port Config + + + + + Preferences + + + + + Minimize + + + + + Maximize/Restore + + + + + Minimize All + + + + + Maximize/Restoe All + + + + + Arrange All - Cascade + + + + + Arrange All - Tile + + + + + Dock + + + + + Help + + + + + About + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..58685b0 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/mythread.cpp b/client/mythread.cpp new file mode 100644 index 0000000..69fe660 --- /dev/null +++ b/client/mythread.cpp @@ -0,0 +1,24 @@ +#include "mythread.h" + + void MyThread::run() + { + int i, j; + + for (i=0; i + +#define NumPorts 2 +#define NumStats 8 + + class MyThread : public QThread + { + Q_OBJECT + + public: + void run(); + + signals: + void portStatsUpdate(int port, void *stats); + + private: + int stats[2][8]; + }; + +#endif diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..3d27500 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,42 @@ +TEMPLATE = app +CONFIG += qt debug +QT += network +RESOURCES += ostinato.qrc +HEADERS += \ + hexlineedit.h \ + mainwindow.h \ + mythread.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatswindow.h \ + portswindow.h \ + streamconfigdialog.h \ + streammodel.h + +FORMS += \ + mainwindow.ui \ + portstatswindow.ui \ + portswindow.ui \ + streamconfigdialog.ui + +SOURCES += \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + mythread.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + streamconfigdialog.cpp \ + streammodel.cpp + +# TODO(LOW): Test only +include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..59f4930 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,21 @@ + + + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/magnifier.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..755e52e --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,24 @@ +#include "port.h" + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + mPortGroupId = portGroupId; + + mAdminStatus = AdminDisable; + mOperStatus = OperDown; + mControlMode = ControlShared; + + // FIXME(HI): TEST only + for(int i = 0; i < 10; i++) + mPortStats[i] = mPortGroupId*10000+mPortId*100+i; +} + +void Port::insertDummyStreams() +{ + mStreams.append(*(new Stream)); + mStreams[0].setName(QString("%1:%2:0").arg(portGroupId()).arg(id())); + mStreams.append(*(new Stream)); + mStreams[1].setName(QString("%1:%2:1").arg(portGroupId()).arg(id())); +} + diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..13795ff --- /dev/null +++ b/client/port.h @@ -0,0 +1,55 @@ +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include +#include "stream.h" + +class Port { + + friend class StreamModel; + friend class PortStatsModel; + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + enum OperStatus { OperDown, OperUp }; + enum ControlMode { ControlShared, ControlExclusive }; + +private: + quint32 mPortId; + quint32 mPortGroupId; + QList mStreams; + QString mName; + QString mDescription; + QString mUserAlias; // user defined + AdminStatus mAdminStatus; + OperStatus mOperStatus; + ControlMode mControlMode; + + quint32 mPortStats[10]; // FIXME(HI):Hardcoding + +public: + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + + quint32 id() const { return mPortId; } + quint32 portGroupId() const { return mPortGroupId; } + const QString& name() const { return mName; } + const QString& description() const { return mDescription; } + const QString& userAlias() const { return mUserAlias; } + + void setName(QString &name) { mName = name; } + void setName(const char* name) { mName = QString(name); } + void setDescription(QString &description) { mDescription = description; } + void setDescription(const char *description) + { mDescription = QString(description); } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + // FIXME(HIGH): Only for testing + void insertDummyStreams(); +}; + +#endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..b7f8eb3 --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,169 @@ +#include "portgroup.h" +#include "../common/protocol.h" + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + // Init attributes for which we values were passed to us + mServerAddress = ip; + mServerPort = port; + + // Init remaining attributes with defaults + mpSocket = new QTcpSocket(this); + + // TODO: consider using QT's signal-slot autoconnect + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_mpSocket_stateChanged())); + connect(mpSocket, SIGNAL(connected()), this, SLOT(when_connected())); + connect(mpSocket, SIGNAL(disconnected()), this, SLOT(when_disconnected())); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(when_error(QAbstractSocket::SocketError))); + connect(mpSocket, SIGNAL(readyRead()), this, SLOT(when_dataAvail())); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free TCP mpSocketet etc. + PortGroup::disconnectFromHost(); + delete mpSocket; +} + +void PortGroup::connectToHost(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + + PortGroup::connectToHost(); +} + +void PortGroup::connectToHost() +{ + qDebug("PortGroup::connectToHost()"); + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PortGroup::disconnectFromHost() +{ + mpSocket->disconnectFromHost(); +} + +// -------------------------------------------- +// Private Methods +// -------------------------------------------- +void PortGroup::ProcessMsg(const char *msg, quint32 size) +{ + tCommHdr *hdr; + // TODO: For now, assuming we'll get a complete msg + // but need to fix this as this is a TCP stream + + hdr = (tCommHdr*) msg; + + if (hdr->ver != 1) // FIXME:hardcoding + { + qDebug("Rcvd msg with invalid version %d\n", hdr->ver); + goto _exit; + } + + qDebug("msgType - %x\n", NTOHS(hdr->msgType)); + switch (NTOHS(hdr->msgType)) + { + case e_MT_CapabilityInfo: + ProcessCapabilityInfo(msg+sizeof(tCommHdr), + NTOHS(hdr->msgLen)-sizeof(tCommHdr)); + break; + + default: + qDebug("Rcvd msg with unrecognized msgType %d\n", NTOHS(hdr->msgType)); + } + +_exit: + return; +} + +void PortGroup::ProcessCapabilityInfo(const char *msg, qint32 size) +{ + tTlvPortCapability *cap = (tTlvPortCapability*) msg; + Port *p; + + emit portListAboutToBeChanged(mPortGroupId); + + while (size) + { + qDebug("size = %d, tlvType = %d, tlvLen = %d\n", + size, NTOHS(cap->tlvType), NTOHS(cap->tlvLen)); + if (NTOHS(cap->tlvType) != e_TT_PortCapability) + { + qDebug("Unrecognized TLV Type %d\n", NTOHS(cap->tlvType)); + goto _next; + } + + p = new Port(NTOHL(cap->port), mPortGroupId); + p->setName(cap->name); + p->setDescription(cap->desc); + p->insertDummyStreams(); // FIXME: only for testing + qDebug("before port append\n"); + mPorts.append(*p); + +_next: + size -= NTOHS(cap->tlvLen); + cap = (tTlvPortCapability*)((char *)(cap) + NTOHS(cap->tlvLen)); + } + + emit portListChanged(mPortGroupId); + + return; +} + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_mpSocket_stateChanged() +{ + qDebug("state changed"); + emit portGroupDataChanged(this); +} + +void PortGroup::when_connected() +{ + qDebug("connected\n"); + + emit portGroupDataChanged(this); + + // Ask for Port Capability + tCommHdr pkt; + pkt.ver = 1; + pkt.resv1 = 0; + pkt.resv2 = 0; + pkt.msgType = HTONS(e_MT_CapabilityReq); + pkt.msgLen = HTONS(8); + + mpSocket->write((char*) &pkt, sizeof(pkt)); +} + +void PortGroup::when_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + mPorts.clear(); + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(this); +} + +void PortGroup::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("error\n"); + emit portGroupDataChanged(this); +} + +void PortGroup::when_dataAvail() +{ + qDebug("dataAvail\n"); + + QByteArray msg = mpSocket->read(1024); // FIXME: hardcoding + ProcessMsg(msg.constData(), msg.size()); +} + + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..8f5c1e3 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,66 @@ +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class PortGroup : public QObject { + Q_OBJECT + +private: + quint32 mPortGroupId; + static quint32 mPortGroupAllocId; + QString mUserAlias; // user defined + + QTcpSocket *mpSocket; + QHostAddress mServerAddress; + quint16 mServerPort; +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost(); + void connectToHost(QHostAddress ip, quint16 port); + void disconnectFromHost(); + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + const QString& userAlias() const { return mUserAlias; } + QAbstractSocket::SocketState state() const { return mpSocket->state(); } + + void setUserAlias(QString alias) { mUserAlias = alias; }; + +signals: + void portGroupDataChanged(PortGroup* portGroup); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + +private slots: + void on_mpSocket_stateChanged(); + void when_connected(); + void when_disconnected(); + void when_error(QAbstractSocket::SocketError socketError); + void when_dataAvail(); + +private: + void ProcessCapabilityInfo(const char *msg, qint32 size); + void ProcessMsg(const char *msg, quint32 size); +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..06df02d --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,108 @@ +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + + // TODO(LOW): Remove + new ModelTest(getStreamModel()); + new ModelTest(getPortModel()); + new ModelTest(getPortStatsModel()); + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + if (mPortGroupListModel.isPortGroup(index)) + { + //return *(mPortGroups[mPortGroupListModel.portGroupId(index)]); + return *(mPortGroups[index.row()]); + } +#if 0 // FIXME(MED) + else + return NULL; +#endif +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + if (mPortGroupListModel.isPort(index)) + { + return (mPortGroups.at(index.parent().row())-> + mPorts[index.row()]); + } +#if 0 // FIXME(MED) + else + return NULL; +#endif +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..1e5add9 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,47 @@ +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + +// Methods +public: + PortGroupList::PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portgrouplistmodel.h b/client/portgrouplistmodel.h new file mode 100644 index 0000000..4242539 --- /dev/null +++ b/client/portgrouplistmodel.h @@ -0,0 +1,55 @@ +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include + +/* ----------------------------------------------------------- +** NOTE: FILE NOT USED 'COZ MOC DOESN'T SUPPORT NESTED CLASSES +*-------------------------------------------------------------*/ + + +class PortGroupList; + +class PortGroupList : public QObject { + + Q_OBJECT + + class PortListModel : public QAbstractItemModel + { + // This is currently a read only model + Q_OBJECT + + PortGroupList *pgl; // FIXME(HIGH): rename member + + public: + PortListModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + + friend class PortGroupList; // FIXME(MED): Review need for friend + }; + + friend class PortListModel; + + QList mPortGroups; + PortListModel mPortGroupListModel; + +// Methods +public: + PortGroupList::PortGroupList(); + QAbstractItemModel* getModel(); + int addPortGroup(PortGroup &portGroup); + int removePortGroup(PortGroup &portGroup); + +private slots: + void when_portlist_dataChanged(); +}; + +#endif diff --git a/client/portlistmodel.cpp b/client/portlistmodel.cpp new file mode 100644 index 0000000..df53fb9 --- /dev/null +++ b/client/portlistmodel.cpp @@ -0,0 +1,191 @@ +#include "portlistmodel.h" + +----------------------- +This file is not used +----------------------- + +PortListModel::PortListModel(QObject *parent) + : QAbstractItemModel(parent) +{ + portList = new QList; + +#if 0 // FIXME: Dummy data only for testing; to be removed + PortGroup pg; + + pg.name = "A"; + pg.isLocal = TRUE; + pg.numPorts = 3; + pg.port = new Port[pg.numPorts]; + pg.port[0].portId = 0; + pg.port[0].name = "A0"; + pg.port[0].desc = "a0a0a0a0a0a0"; + pg.port[1].portId = 1; + pg.port[1].name = "A1"; + pg.port[1].desc = "a1a1a1a1a1a1"; + pg.port[2].portId = 2; + pg.port[2].name = "A2"; + pg.port[2].desc = "a2a2a2a2a2a2"; + + portList->append(pg); + + pg.name = "B"; + pg.isLocal = FALSE; + pg.numPorts = 2; + pg.port = new Port[pg.numPorts]; + pg.port[0].portId = 0; + pg.port[0].name = "B0"; + pg.port[0].desc = "b0b0b0b0b0b0"; + pg.port[1].portId = 1; + pg.port[1].name = "B1"; + pg.port[1].desc = "b1b1b1b1b1b1"; + + portList->append(pg); +#endif + // Do I need to do anything here? +} + +int PortListModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + // qDebug("RowCount top\n"); + // qDebug("RowCount Exit: %d\n", portList->size()); + return portList->size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { + // qDebug("RowCount Exit: %d\n", portList->at(parent.row()).numPorts); + return portList->at(parent.row()).numPorts; + } + else + { + // Leaf Item + return 0; + } +} + +int PortListModel::columnCount(const QModelIndex &parent ) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortListModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortListModel::data(const QModelIndex &index, int role) const +{ + //qDebug("Enter PortListModel data\n"); + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + + // Check role + if ((role == Qt::DisplayRole)) + { +#if 0 // Only for debug + qDebug("Exit PortListModel data\n"); + return "Testing"; // FIXME: for dbg only +#endif + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item + return QString("%1 (%2) [%3]"). + arg(portList->at(index.row()).name). + arg(portList->at(index.row()).isLocal == TRUE? "LOCAL" : "REMOTE"). + arg(portList->at(index.row()).numPorts); + } + return QString("%1: %2 (%3)"). + arg(portList->at(parent.row()).port[index.row()].portId). + arg(portList->at(parent.row()).port[index.row()].name). + arg(portList->at(parent.row()).port[index.row()].desc); + } + else + return QVariant(); +} + +QVariant PortListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("%1").arg(section+1); +} + +QModelIndex PortListModel::index (int row, int col, + const QModelIndex & parent) const +{ +#if 0 + if (!hasIndex(row, col, parent)) + return QModelIndex(); +#endif + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = row, p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.row(), p = row; + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortListModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", + // pg, 0, id); + + return createIndex(pg, 0, id); + +} + +void PortListModel::doRefresh() +{ + emit layoutChanged(); +} + + diff --git a/client/portlistmodel.h b/client/portlistmodel.h new file mode 100644 index 0000000..45a07bc --- /dev/null +++ b/client/portlistmodel.h @@ -0,0 +1,37 @@ +#ifndef _PORT_LIST_MODEL_H +#define _PORT_LIST_MODEL_H + + + ----------------- + This file is not used + ------------------ + +#include +#include +#include "portgroup.h" + +static QStringList PortListCols = (QStringList() + << "Name" +); + +class PortListModel : public QAbstractItemModel +{ + Q_OBJECT + + public: + //PortListModel(QObject *parent = 0) : QAbstractItemModel(parent) {} + PortListModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + + void doRefresh(); // FIXME: temp till model exports functions to insert rows + QList *portList; // FIXME: this shd be private +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..61f9be8 --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,314 @@ +#include "portmodel.h" +#include "portgrouplist.h" +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &parent ) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 4\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); + + return QString("Port %1: %2 [%3] (%4)"). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()].id()). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()].name()). + arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()].description()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 5\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); + return QIcon(":/icons/bullet_green.png"); + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } +} + +QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row).id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if ((index.internalId() & 0xFFFF) == 0xFFFF) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if ((index.internalId() & 0xFFFF) != 0xFFFF) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(PortGroup* portGroup) +{ + QModelIndex index; + + if (!pgl->mPortGroups.contains(portGroup)) + { + qDebug("when_portGroupDataChanged(): pg not in list - do nothing"); + return; + } + + qDebug("when_portGroupDataChanged pgid = %d", portGroup->id()); + qDebug("when_portGroupDataChanged idx = %d", pgl->mPortGroups.indexOf(portGroup)); + + index = createIndex(pgl->mPortGroups.indexOf(portGroup), 0, + (portGroup->id() << 16) | 0xFFFF); + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +#if 0 +void PortModel::triggerLayoutAboutToBeChanged() +{ + emit layoutAboutToBeChanged(); +} + +void PortModel::triggerLayoutChanged() +{ + emit layoutChanged(); +} +#endif + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..b68acdc --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,51 @@ +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + + PortGroupList *pgl; + + public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + + +private slots: + void when_portGroupDataChanged(PortGroup *portGroup); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif + +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..3dae66d --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,124 @@ + + Dialog + + + + 0 + 0 + 402 + 275 + + + + Dialog + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + + + + + < + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvAllPorts + tbFilterIn + tbFilterOut + lvFilteredPorts + buttonBox + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..8845dd2 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,9 @@ +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(AbstractItemModel *allPortsModel, + QWidget *parent) +{ + setupUi(this); + + lvAllPorts->setModel(allPortsModel); +} diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..c180f34 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,19 @@ +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include "ui_portstatsfilterdialog.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + PortStatsFilterDialog(AbstractItemModel *allPortsModel, + QWidget *parent = 0); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..c67c7e4 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,119 @@ +#include "portstatsmodel.h" +#include "portgrouplist.h" + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + int pgidx, pidx, portNum; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (pgidx = 0; pgidx < numPorts.size(); pgidx++) + if (portNum <= numPorts.at(pgidx)) + break; + + if (pgidx) + { + if (numPorts.at(pgidx -1)) + pidx = (portNum - 1) % numPorts.at(pgidx - 1); + else + pidx = portNum - 1; + } + else + pidx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + return pgl->mPortGroups.at(pgidx)->mPorts.at(pidx).mPortStats[index.row()]; + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QString("Port %1").arg(section); + else + return PortStatName.at(section); +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void*stats) +{ + // FIXME(MED): update only the changed port not all + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + + diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..6bf5c5a --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,59 @@ +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +typedef enum { + e_STAT_FRAMES_RCVD = 0, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + PortGroupList *pgl; + + public: + PortStatsModel(PortGroupList *p, 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; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + + private: + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..594209e --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,15 @@ +#include "portstatswindow.h" +#include "portstatsmodel.h" + +//PortStatsWindow::PortStatsWindow(QWidget *parent) : QDialog (parent) +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) +{ + setupUi(this); + + tvPortStats->setModel(pgl->getPortStatsModel()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..2cec09a --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,21 @@ +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" + +class PortStatsWindow : public QDialog, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); +private: + QAbstractItemModel *model; +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..f3dff15 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,133 @@ + + 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 + + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..c316b0d --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,320 @@ +#include "portswindow.h" +#include "streamlistmodel.h" +#include "streamconfigdialog.h" +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) +{ + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionDelete_Stream); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + connect( tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_streamView_currentChanged(const QModelIndex&, + const QModelIndex&))); + connect( tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_streamView_selectionChanged())); + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); +} + +PortsWindow::~PortsWindow() +{ + delete plm; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + // FIXME(MED): This way of passing params must be changed + scd = new StreamConfigDialog(plm->getStreamModel()->currentPortStreamList(), + (uint) index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + updatePortViewActions(current); + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } +} + +void PortsWindow::when_streamView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + qDebug("stream view current changed"); + updateStreamViewActions(); +} + +void PortsWindow::when_streamView_selectionChanged() +{ + qDebug("stream view selection changed"); + updateStreamViewActions(); +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if ((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex()) && + ((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight)) + { + updatePortViewActions(tvPortList->currentIndex()); + } + +} + +#if 0 +void PortsWindow::updateStreamViewActions(const QModelIndex& current) +{ + if (current.isValid()) + actionDelete_Stream->setEnabled(true); + else + actionDelete_Stream->setDisabled(true); +} +#endif + +void PortsWindow::updateStreamViewActions() +{ + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + // If more than one non-contiguous ranges selected, disable "New" + if (tvStreamList->selectionModel()->selection().size() > 1) + actionNew_Stream->setDisabled(true); + else + actionNew_Stream->setEnabled(true); + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + actionNew_Stream->setEnabled(true); + actionDelete_Stream->setDisabled(true); + } +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setEnabled(false); + actionConnect_Port_Group->setEnabled(false); + actionDisconnect_Port_Group->setEnabled(false); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ +{ + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +} +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} +#if 0 +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + int row = 0; + + if (tvStreamList->currentIndex().isValid()) + row = tvStreamList->currentIndex().row(); + plm->getStreamModel()->insertRows(row, 1); +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + if (tvStreamList->currentIndex().isValid()) + plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); +} +#endif + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..2a26c2b --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,56 @@ +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + + void updatePortViewActions(const QModelIndex& current); + //void updateStreamViewActions(const QModelIndex& current); + void updateStreamViewActions(); + +private slots: + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_selectionChanged(); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionDelete_Stream_triggered(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..04349d2 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,214 @@ + + PortsWindow + + + + 0 + 0 + 689 + 320 + + + + Form + + + + + + Qt::Horizontal + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + + + + + Qt::Horizontal + + + + 31 + 41 + + + + + + + + Capacity + + + + + + + + + + Aggr fps + + + + + + + + + + % age + + + + + + + + + + Aggr bps + + + + + + + + + + + + 0 + + + + Control + + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + + Port Group Detail + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + + + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..980d26c --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,123 @@ +#include + +Stream::Stream() +{ + // Default constructor + InitDefaultMeta(); + InitDefaultProto(); + InitDefaultL2(); + InitDefaultL3(); + InitDefaultL4(); +} + +void Stream::InitDefaultMeta() +{ + // TODO(LOW): Use #defines + meta.patternMode = e_dp_fixed; + meta.pattern = 0x00000000; + meta.dataStartOfs = 0; // FIXME(HIGH): this has to calculated + meta.lenMode = e_fl_fixed; + meta.frameLen = 64; + meta.frameLenMin = 64; + meta.frameLenMax = 1518; +} + +void Stream::InitDefaultProto() +{ + // TODO(LOW): Use #defines + proto.ft = e_ft_eth_2; + proto.dsap = 0x00; + proto.ssap = 0x00; + proto.ctl = 0x00; + proto.ouiMsb = 0x00; + proto.ouiLshw = 0x0000; + + proto.protoMask = PM_L3_PROTO_NONE | PM_L4_PROTO_NONE; + proto.etherType = ETH_TYP_IP; + proto.ipProto = IP_PROTO_TCP; +} + + +void Stream::InitDefaultL2() +{ + // TODO(LOW): Use #defines + l2.eth.dstMacMshw = 0x0000; + l2.eth.dstMacLsw = 0x00000001; + l2.eth.dstMacMode = e_mm_fixed; + l2.eth.dstMacCount = 16; + l2.eth.dstMacStep = 1; + + l2.eth.srcMacMshw = 0x0000; + l2.eth.srcMacLsw = 0x00000002; + l2.eth.srcMacMode = e_mm_fixed; + l2.eth.srcMacCount = 16; + l2.eth.srcMacStep = 1; + + l2.eth.vlanMask = VM_UNTAGGED; + l2.eth.ctpid = 0x8100; + l2.eth.cvlanPrio = 0; + l2.eth.cvlanCfi = 0; + l2.eth.cvlanId = 2; + l2.eth.stpid = 0x88a8; + l2.eth.svlanPrio = 0; + l2.eth.svlanCfi = 0; + l2.eth.svlanId = 2; +} + +void Stream::InitDefaultL3() +{ + InitDefaultL3Ip(); +} + +void Stream::InitDefaultL3Ip() +{ + l3.ip.ipMask = STREAM_DEF_IP_MASK; + l3.ip.ver = STREAM_DEF_L3_IP_VER; + l3.ip.hdrLen = STREAM_DEF_L3_IP_HDR_LEN; + l3.ip.tos = STREAM_DEF_L3_IP_TOS; + l3.ip.totLen = STREAM_DEF_L3_IP_TOT_LEN; + l3.ip.id = STREAM_DEF_L3_IP_ID; + l3.ip.flags = STREAM_DEF_L3_IP_FLAGS; + l3.ip.fragOfs = STREAM_DEF_L3_IP_FRAG_OFS; + l3.ip.ttl = STREAM_DEF_L3_IP_TTL; + l3.ip.proto = STREAM_DEF_L3_IP_PROTO; + l3.ip.cksum = STREAM_DEF_L3_IP_CKSUM; + l3.ip.srcIp = STREAM_DEF_L3_IP_SRC_IP; + l3.ip.srcIpMode = STREAM_DEF_L3_IP_SRC_IP_MODE; + l3.ip.srcIpCount = STREAM_DEF_L3_IP_SRC_IP_COUNT; + l3.ip.srcIpMask = STREAM_DEF_L3_IP_SRC_IP_MASK; + l3.ip.dstIp = STREAM_DEF_L3_IP_DST_IP; + l3.ip.dstIpMode = STREAM_DEF_L3_IP_DST_IP_MODE; + l3.ip.dstIpCount = STREAM_DEF_L3_IP_DST_IP_COUNT; + l3.ip.dstIpMask = STREAM_DEF_L3_IP_DST_IP_MASK; +} + +void Stream::InitDefaultL4() +{ + InitDefaultL4Tcp(); + InitDefaultL4Udp(); +} + +void Stream::InitDefaultL4Tcp() +{ + l4.tcp.tcpMask = STREAM_DEF_L4_TCP_TCP_MASK; + l4.tcp.srcPort = STREAM_DEF_L4_TCP_SRC_PORT; + l4.tcp.dstPort = STREAM_DEF_L4_TCP_DST_PORT; + l4.tcp.seqNum = STREAM_DEF_L4_TCP_SEQ_NUM; + l4.tcp.ackNum = STREAM_DEF_L4_TCP_ACK_NUM; + l4.tcp.hdrLen = STREAM_DEF_L4_TCP_HDR_LEN; + l4.tcp.rsvd = STREAM_DEF_L4_TCP_RSVD; + l4.tcp.flags = STREAM_DEF_L4_TCP_FLAGS; + l4.tcp.window = STREAM_DEF_L4_TCP_WINDOW; + l4.tcp.cksum = STREAM_DEF_L4_TCP_CKSUM; + l4.tcp.urgPtr = STREAM_DEF_L4_TCP_URG_PTR; +} + +void Stream::InitDefaultL4Udp() +{ + l4.udp.udpMask = STREAM_DEF_L4_UDP_UDP_MASK; + l4.udp.srcPort = STREAM_DEF_L4_UDP_SRC_PORT; + l4.udp.dstPort = STREAM_DEF_L4_UDP_DST_PORT; + l4.udp.totLen = STREAM_DEF_L4_UDP_TOT_LEN; + l4.udp.cksum = STREAM_DEF_L4_UDP_CKSUM; +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..c1f9d5f --- /dev/null +++ b/client/stream.h @@ -0,0 +1,307 @@ +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include + +class StreamConfigDialog; +class StreamModel; + +class Stream { + + friend class StreamConfigDialog; + friend class StreamModel; + + enum FrameType { + e_ft_none, + e_ft_eth_2, + e_ft_802_3_raw, + e_ft_802_3_llc, + e_ft_snap + }; + + enum DataPatternMode { + e_dp_fixed, + e_dp_inc, + e_dp_dec, + e_dp_random + }; + + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum MacAddrMode { + e_mm_fixed, + e_mm_inc, + e_mm_dec, + }; + + enum IpAddrMode { + e_im_fixed, + e_im_inc_host, + e_im_dec_host, + e_im_random_host + }; + + // Meta Data + struct { + // Data Pattern + DataPatternMode patternMode; + quint32 pattern; + quint16 dataStartOfs; + + // Frame Length (includes CRC) + FrameLengthMode lenMode; + quint16 frameLen; + quint16 frameLenMin; + quint16 frameLenMax; + } meta; + + // Protocols + struct { + FrameType ft; + + quint8 dsap; + quint8 ssap; + quint8 ctl; + quint8 ouiMsb; + quint16 ouiLshw; + + quint16 protoMask; +#define PM_L3_PROTO_NONE 0x0001 +#define PM_L3_PROTO_OTHER 0x0002 +#define PM_L4_PROTO_NONE 0x0004 +#define PM_L4_PROTO_OTHER 0x0008 + + quint16 etherType; +#define ETH_TYP_IP 0x0800 +#define ETH_TYP_ARP 0x0806 + + quint16 ipProto; +#define IP_PROTO_ICMP 0x01 +#define IP_PROTO_IGMP 0x02 +#define IP_PROTO_TCP 0x06 +#define IP_PROTO_UDP 0x11 + } proto; + + // L2 + struct { + // Ethernet + struct { + // Dst Mac + quint16 dstMacMshw; + quint32 dstMacLsw; + MacAddrMode dstMacMode; + quint16 dstMacCount; + quint16 dstMacStep; + + // srcMac + quint16 srcMacMshw; + quint32 srcMacLsw; + MacAddrMode srcMacMode; + quint16 srcMacCount; + quint16 srcMacStep; + + + quint16 vlanMask; +#define VM_UNTAGGED 0x0000 +#define VM_CVLAN_TAGGED 0x0001 +#define VM_CVLAN_TPID_OVERRIDE 0x0002 +#define VM_SVLAN_TAGGED 0x0100 +#define VM_SVLAN_TPID_OVERRIDE 0x0200 + + quint16 ctpid; + quint16 cvlanPrio : 3; + quint16 cvlanCfi : 1; + quint16 cvlanId : 13; + quint16 stpid; + quint16 svlanPrio : 3; + quint16 svlanCfi : 1; + quint16 svlanId : 13; + } eth; + } l2; + + struct { + // IP + struct { + quint8 ipMask; +#define IM_OVERRIDE_VERSION 0x01 +#define IM_OVERRIDE_HDRLEN 0x02 +#define IM_OVERRIDE_TOTLEN 0x04 +#define IM_OVERRIDE_CKSUM 0x08 +#define STREAM_DEF_IP_MASK 0x00 + + quint8 ver : 4; +#define STREAM_DEF_L3_IP_VER 0x4 + + quint8 hdrLen : 4; +#define STREAM_DEF_L3_IP_HDR_LEN 0x5 + + quint8 tos; +#define STREAM_DEF_L3_IP_TOS 0x00 + + quint16 totLen; +#define STREAM_DEF_L3_IP_TOT_LEN 0x00 + + quint16 id; +#define STREAM_DEF_L3_IP_ID 0x1234 + + quint16 flags : 3; +#define IP_FLAG_UNUSED 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_MF 0x4 +#define STREAM_DEF_L3_IP_FLAGS 0x00 + + quint16 fragOfs : 13; +#define STREAM_DEF_L3_IP_FRAG_OFS 0x0000 + + quint8 ttl; +#define STREAM_DEF_L3_IP_TTL 0x7F + + quint8 proto; +#define STREAM_DEF_L3_IP_PROTO 0x00 + + quint16 cksum; +#define STREAM_DEF_L3_IP_CKSUM 0x0000 + + // Source IP + quint32 srcIp; +#define STREAM_DEF_L3_IP_SRC_IP 0x02020202 + + IpAddrMode srcIpMode; +#define STREAM_DEF_L3_IP_SRC_IP_MODE e_im_fixed + + quint16 srcIpCount; +#define STREAM_DEF_L3_IP_SRC_IP_COUNT 16 + + quint32 srcIpMask; +#define STREAM_DEF_L3_IP_SRC_IP_MASK 0xFFFFFFFF + + // Destination IP + quint32 dstIp; +#define STREAM_DEF_L3_IP_DST_IP 0x01010101 + + IpAddrMode dstIpMode; +#define STREAM_DEF_L3_IP_DST_IP_MODE e_im_fixed + + quint16 dstIpCount; +#define STREAM_DEF_L3_IP_DST_IP_COUNT 16 + + quint32 dstIpMask; +#define STREAM_DEF_L3_IP_DST_IP_MASK 0xFFFFFFFF + + // TODO: Options + } ip; + + // TODO: ARP + struct { + } arp; + } l3; + + // L4 + struct { + // TCP + struct { + quint32 tcpMask; +#define TM_OVERRIDE_HDRLEN 0x1 +#define TM_OVERRIDE_CKSUM 0x2 +#define STREAM_DEF_L4_TCP_TCP_MASK 0x00; + + quint16 srcPort; +#define STREAM_DEF_L4_TCP_SRC_PORT 8902; + + quint16 dstPort; +#define STREAM_DEF_L4_TCP_DST_PORT 80 + + quint32 seqNum; +#define STREAM_DEF_L4_TCP_SEQ_NUM 129018 + + quint32 ackNum; +#define STREAM_DEF_L4_TCP_ACK_NUM 98223 + + quint8 hdrLen : 4; +#define STREAM_DEF_L4_TCP_HDR_LEN 0x5 + + quint8 rsvd : 4; +#define STREAM_DEF_L4_TCP_RSVD 0x0 + + quint8 flags; +#define TCP_FLAG_URG 0x01 +#define TCP_FLAG_ACK 0x02 +#define TCP_FLAG_PSH 0x04 +#define TCP_FLAG_RST 0x08 +#define TCP_FLAG_SYN 0x10 +#define TCP_FLAG_FIN 0x20 +#define STREAM_DEF_L4_TCP_FLAGS 0x00 + + + quint16 window; +#define STREAM_DEF_L4_TCP_WINDOW 1024 + + quint16 cksum; +#define STREAM_DEF_L4_TCP_CKSUM 0x0000 + + quint16 urgPtr; +#define STREAM_DEF_L4_TCP_URG_PTR 0x0000 + } tcp; + + // UDP + struct { + quint32 udpMask; +#define UM_OVERRIDE_TOTLEN 0x01 +#define UM_OVERRIDE_CKSUM 0x02 +#define STREAM_DEF_L4_UDP_UDP_MASK 0x00 + + quint16 srcPort; +#define STREAM_DEF_L4_UDP_SRC_PORT 8902 + + quint16 dstPort; +#define STREAM_DEF_L4_UDP_DST_PORT 80 + + quint16 totLen; +#define STREAM_DEF_L4_UDP_TOT_LEN 0x0000 + + quint16 cksum; +#define STREAM_DEF_L4_UDP_CKSUM 0x0000 + } udp; + + // TODO: ICMP + struct { + } icmp; + + // TODO: IGMP + struct { + } igmp; + } l4; + + QString mName; + bool mIsEnabled; + +// ------------------------------------------------------- +// Methods +// ------------------------------------------------------- +public: + Stream(); + int enable(bool flag); + const QString& name() const { return mName; } + bool isEnabled() const { return mIsEnabled; } + + void setName(QString name) { mName = name; } + void setEnabled(bool isEnabled) { mIsEnabled = isEnabled; } + +private: + void InitDefaultMeta(); + void InitDefaultProto(); + void InitDefaultL2(); + void InitDefaultL3(); + void InitDefaultL3Ip(); + void InitDefaultL4(); + void InitDefaultL4Tcp(); + void InitDefaultL4Udp(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..f29592f --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,677 @@ +#include +#include "streamconfigdialog.h" +#include "stream.h" + +StreamConfigDialog::StreamConfigDialog(QList *streamList, + uint streamIndex, QWidget *parent) : QDialog (parent) +{ + setupUi(this); + setupUiExtra(); + + // FIXME(MED): Assumption that streamlist and streamIndex are valid + mpStreamList = streamList; + mCurrentStreamIndex = streamIndex; + LoadCurrentStream(); + + qDebug("stream %p %d/%d loaded", + mpStreamList, mCurrentStreamIndex, mpStreamList->size()); + +// FIXME(MED): Enable this navigation +#if 0 + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // Setup default stuff that cannot be done in designer + twProto->setTabEnabled(2, FALSE); + twProto->setTabEnabled(3, FALSE); + + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + + // L2 Ethernet + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leCvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); + leSvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); + //leEtherType->setValidator(new QRegExpValidator(reHex2B, this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + + // Show "Packet Config" page by default + twTopLevel->setCurrentIndex(0); +} + +StreamConfigDialog::~StreamConfigDialog() +{ +} + +void StreamConfigDialog::on_cmbDstMacMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + leDstMacCount->setEnabled(FALSE); + else + leDstMacCount->setEnabled(TRUE); +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_rbFtLlcSnap_toggled(bool checked) +{ + if (checked) + { + leDsap->setText("AA"); + leSsap->setText("AA"); + leControl->setText("03"); + } +} + +void StreamConfigDialog::on_rbL3Ipv4_toggled(bool checked) +{ + if (checked) + { + swL3Proto->setCurrentIndex(0); + twProto->setTabEnabled(2, TRUE); + twProto->setTabText(2, "L3 (IPv4)"); + leType->setText("08 00"); + } + else + { + twProto->setTabEnabled(2, FALSE); + twProto->setTabText(2, "L3"); + } +} + +void StreamConfigDialog::on_rbL3Arp_toggled(bool checked) +{ + if (checked) + { + swL3Proto->setCurrentIndex(1); + twProto->setTabEnabled(2, TRUE); + twProto->setTabText(2, "L3 (ARP)"); + leType->setText("08 06"); + } + else + { + twProto->setTabEnabled(2, FALSE); + twProto->setTabText(2, "L3"); + } +} + +void StreamConfigDialog::on_rbL4Icmp_toggled(bool checked) +{ + QString str; + + if (checked) + { + swL4Proto->setCurrentIndex(2); + twProto->setTabEnabled(3, TRUE); + twProto->setTabText(3, "L4 (ICMP)"); + leIpProto->setText(uintToHexStr(IP_PROTO_ICMP, str, 1)); + } + else + { + twProto->setTabEnabled(3, FALSE); + twProto->setTabText(3, "L4"); + } +} + +void StreamConfigDialog::on_rbL4Igmp_toggled(bool checked) +{ + QString str; + + if (checked) + { + swL4Proto->setCurrentIndex(3); + twProto->setTabEnabled(3, TRUE); + twProto->setTabText(3, "L4 (IGMP)"); + leIpProto->setText(uintToHexStr(IP_PROTO_IGMP, str, 1)); + } + else + { + twProto->setTabEnabled(3, FALSE); + twProto->setTabText(3, "L4"); + } +} + +void StreamConfigDialog::on_rbL4Tcp_toggled(bool checked) +{ + QString str; + + if (checked) + { + swL4Proto->setCurrentIndex(0); + twProto->setTabEnabled(3, TRUE); + twProto->setTabText(3, "L4 (TCP)"); + leIpProto->setText(uintToHexStr(IP_PROTO_TCP, str, 1)); + } + else + { + twProto->setTabEnabled(3, FALSE); + twProto->setTabText(3, "L4"); + } +} + +void StreamConfigDialog::on_rbL4Udp_toggled(bool checked) +{ + QString str; + + if (checked) + { + swL4Proto->setCurrentIndex(1); + twProto->setTabEnabled(3, TRUE); + twProto->setTabText(3, "L4 (UDP)"); + leIpProto->setText(uintToHexStr(IP_PROTO_UDP, str, 1)); + } + else + { + twProto->setTabEnabled(3, FALSE); + twProto->setTabText(3, "L4"); + } +} + +void StreamConfigDialog::on_rbL4Other_toggled(bool checked) +{ + if (checked) + leIpProto->setEnabled(true); + else + leIpProto->setEnabled(false); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets) +{ + int i; + QChar zero('0'); + + hexStr = ""; + + for (i = octets; i > 0; i--) + { + ushort byte; + QString str1 = "%1"; + QString str; + + byte = num & 0xff; + str = str1.arg(byte, 2, 16, zero).append(' '); + hexStr.prepend(str); + num = num >> 8; + } + + return hexStr; +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +void StreamConfigDialog::LoadCurrentStream() +{ + Stream *pStream = &((*mpStreamList)[mCurrentStreamIndex]); + QString str; + + qDebug("loading pStream %p", pStream); + + // Meta Data + { + cmbPatternMode->setCurrentIndex(pStream->meta.patternMode); + lePattern->setText(uintToHexStr(pStream->meta.pattern, str, 4)); + + cmbPktLenMode->setCurrentIndex(pStream->meta.lenMode); + lePktLen->setText(str.setNum(pStream->meta.frameLen)); + lePktLenMin->setText(str.setNum(pStream->meta.frameLenMin)); + lePktLenMax->setText(str.setNum(pStream->meta.frameLenMax)); + } + + // Protocols + { + qDebug("ft = %d\n", pStream->proto.ft); + switch(pStream->proto.ft) + { + case Stream::e_ft_none: + rbFtNone->setChecked(TRUE); + break; + case Stream::e_ft_eth_2: + rbFtEthernet2->setChecked(TRUE); + break; + case Stream::e_ft_802_3_raw: + rbFt802Dot3Raw->setChecked(TRUE); + break; + case Stream::e_ft_802_3_llc: + rbFt802Dot3Llc->setChecked(TRUE); + break; + case Stream::e_ft_snap: + rbFtLlcSnap->setChecked(TRUE); + break; + } + leDsap->setText(uintToHexStr(pStream->proto.dsap, str, 1)); + leSsap->setText(uintToHexStr(pStream->proto.ssap, str, 1)); + leControl->setText(uintToHexStr(pStream->proto.ctl, str, 1)); + leOui->setText(uintToHexStr((pStream->proto.ouiMsb << 16 + pStream->proto.ouiLshw), str, 3)); + + leType->setText(uintToHexStr(pStream->proto.etherType, str, 2)); + + // Check for specific supported protocols first ... + if (pStream->proto.etherType == ETH_TYP_IP) + rbL3Ipv4->setChecked(TRUE); + else if (pStream->proto.etherType == ETH_TYP_ARP) + rbL3Arp->setChecked(TRUE); + + // ... then for None/Other + rbL3None->setChecked((pStream->proto.protoMask & PM_L3_PROTO_NONE) > 0); + rbL3Other->setChecked((pStream->proto.protoMask & PM_L3_PROTO_OTHER) > 0); + + // Check for specific supported protocols first ... + if (pStream->proto.ipProto == IP_PROTO_ICMP) + rbL4Icmp->setChecked(TRUE); + else if (pStream->proto.ipProto == IP_PROTO_IGMP) + rbL4Igmp->setChecked(TRUE); + else if (pStream->proto.ipProto == IP_PROTO_TCP) + rbL4Tcp->setChecked(TRUE); + else if (pStream->proto.ipProto == IP_PROTO_UDP) + rbL4Udp->setChecked(TRUE); + + // ... then for None/Other + rbL4None->setChecked((pStream->proto.protoMask & PM_L4_PROTO_NONE) > 0); + rbL4Other->setChecked((pStream->proto.protoMask & PM_L4_PROTO_OTHER) > 0); + } + + // L2 + { + // L2 | Ethernet + { + leDstMac->setText(uintToHexStr(pStream->l2.eth.dstMacMshw, str, 2) + + uintToHexStr(pStream->l2.eth.dstMacLsw, str, 4)); + cmbDstMacMode->setCurrentIndex(pStream->l2.eth.dstMacMode); + leDstMacCount->setText(str.setNum(pStream->l2.eth.dstMacCount)); + leDstMacStep->setText(str.setNum(pStream->l2.eth.dstMacStep)); + + leSrcMac->setText(uintToHexStr(pStream->l2.eth.srcMacMshw, str, 2) + + uintToHexStr(pStream->l2.eth.srcMacLsw, str, 4)); + cmbSrcMacMode->setCurrentIndex(pStream->l2.eth.srcMacMode); + leSrcMacCount->setText(str.setNum(pStream->l2.eth.srcMacCount)); + leSrcMacStep->setText(str.setNum(pStream->l2.eth.srcMacStep)); + + cmbCvlanPrio->setCurrentIndex(pStream->l2.eth.cvlanPrio); + cmbCvlanCfi->setCurrentIndex(pStream->l2.eth.cvlanCfi); + leCvlanId->setText(str.setNum(pStream->l2.eth.cvlanId)); + leCvlanTpid->setText(str.setNum(pStream->l2.eth.ctpid)); + cbCvlanTpidOverride->setChecked((pStream->l2.eth.vlanMask & VM_CVLAN_TPID_OVERRIDE) > 0); + gbCvlan->setChecked((pStream->l2.eth.vlanMask & VM_CVLAN_TAGGED) > 0); + + cmbSvlanPrio->setCurrentIndex(pStream->l2.eth.svlanPrio); + cmbSvlanCfi->setCurrentIndex(pStream->l2.eth.svlanCfi); + leSvlanId->setText(str.setNum(pStream->l2.eth.svlanId)); + leSvlanTpid->setText(str.setNum(pStream->l2.eth.stpid)); + cbSvlanTpidOverride->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TPID_OVERRIDE) > 0); + gbSvlan->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) > 0); + } + } + + // L3 + { + // L3 | IP + { + leIpVersion->setText(str.setNum(pStream->l3.ip.ver)); + cbIpVersionOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_VERSION) > 0); + leIpHdrLen->setText(str.setNum(pStream->l3.ip.hdrLen)); + cbIpHdrLenOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_HDRLEN) > 0); + + leIpTos->setText(uintToHexStr(pStream->l3.ip.tos, str, 1)); + + leIpLength->setText(str.setNum(pStream->l3.ip.totLen)); + cbIpLengthOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_TOTLEN) > 0); + + leIpId->setText(uintToHexStr(pStream->l3.ip.id, str, 2)); + leIpFragOfs->setText(str.setNum(pStream->l3.ip.fragOfs)); + cbIpFlagsDf->setChecked((pStream->l3.ip.flags & IP_FLAG_DF) > 0); + cbIpFlagsMf->setChecked((pStream->l3.ip.flags & IP_FLAG_MF) > 0); + + leIpTtl->setText(str.setNum(pStream->l3.ip.ttl)); + leIpProto->setText(uintToHexStr(pStream->l3.ip.proto, str, 1)); + + leIpCksum->setText(uintToHexStr(pStream->l3.ip.cksum, str, 2)); + cbIpCksumOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_CKSUM) > 0); + + leIpSrcAddr->setText(QHostAddress(pStream->l3.ip.srcIp).toString()); + cmbIpSrcAddrMode->setCurrentIndex(pStream->l3.ip.srcIpMode); + leIpSrcAddrCount->setText(str.setNum(pStream->l3.ip.srcIpCount)); + leIpSrcAddrMask->setText(QHostAddress(pStream->l3.ip.srcIpMask).toString()); + + leIpDstAddr->setText(QHostAddress(pStream->l3.ip.dstIp).toString()); + cmbIpDstAddrMode->setCurrentIndex(pStream->l3.ip.dstIpMode); + leIpDstAddrCount->setText(str.setNum(pStream->l3.ip.dstIpCount)); + leIpDstAddrMask->setText(QHostAddress(pStream->l3.ip.dstIpMask).toString()); + } + + // L3 | ARP + { + // TODO + } + } + + // L4 + { + // L4 | TCP + { + leTcpSrcPort->setText(str.setNum(pStream->l4.tcp.srcPort)); + leTcpDstPort->setText(str.setNum(pStream->l4.tcp.dstPort)); + + leTcpSeqNum->setText(str.setNum(pStream->l4.tcp.seqNum)); + leTcpAckNum->setText(str.setNum(pStream->l4.tcp.ackNum)); + + leTcpHdrLen->setText(str.setNum(pStream->l4.tcp.hdrLen)); + cbTcpHdrLenOverride->setChecked((pStream->l4.tcp.tcpMask & TM_OVERRIDE_HDRLEN) > 0); + + leTcpWindow->setText(str.setNum(pStream->l4.tcp.window)); + + leTcpCksum->setText(str.setNum(pStream->l4.tcp.cksum)); + cbTcpCksumOverride->setChecked((pStream->l4.tcp.tcpMask & TM_OVERRIDE_CKSUM) > 0); + + leTcpUrgentPointer->setText(str.setNum(pStream->l4.tcp.urgPtr)); + + cbTcpFlagsUrg->setChecked((pStream->l4.tcp.flags & TCP_FLAG_URG) > 0); + cbTcpFlagsAck->setChecked((pStream->l4.tcp.flags & TCP_FLAG_ACK) > 0); + cbTcpFlagsPsh->setChecked((pStream->l4.tcp.flags & TCP_FLAG_PSH) > 0); + cbTcpFlagsRst->setChecked((pStream->l4.tcp.flags & TCP_FLAG_RST) > 0); + cbTcpFlagsSyn->setChecked((pStream->l4.tcp.flags & TCP_FLAG_SYN) > 0); + cbTcpFlagsFin->setChecked((pStream->l4.tcp.flags & TCP_FLAG_FIN) > 0); + } + + // L4 | UDP + { + leUdpSrcPort->setText(str.setNum(pStream->l4.udp.srcPort)); + leUdpDstPort->setText(str.setNum(pStream->l4.udp.dstPort)); + + leUdpLength->setText(str.setNum(pStream->l4.udp.totLen)); + cbUdpLengthOverride->setChecked((pStream->l4.udp.udpMask & UM_OVERRIDE_TOTLEN) > 0); + + leUdpCksum->setText(str.setNum(pStream->l4.udp.cksum)); + cbUdpCksumOverride->setChecked((pStream->l4.udp.udpMask & UM_OVERRIDE_CKSUM) > 0); + } + + // L4 | ICMP + { + // TODO + } + + // L4 | IGMP + { + // TODO + } + } +} + +void StreamConfigDialog::StoreCurrentStream() +{ + Stream *pStream = &(*mpStreamList)[mCurrentStreamIndex]; + QString str; + bool isOk; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->meta.patternMode = (Stream::DataPatternMode) cmbPatternMode->currentIndex(); + pStream->meta.pattern = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + + pStream->meta.lenMode = (Stream::FrameLengthMode) cmbPktLenMode->currentIndex(); + pStream->meta.frameLen = lePktLen->text().toULong(&isOk); + pStream->meta.frameLenMin = lePktLenMin->text().toULong(&isOk); + pStream->meta.frameLenMax = lePktLenMax->text().toULong(&isOk); + + // Protocols + { + if (rbFtNone->isChecked()) + pStream->proto.ft = Stream::e_ft_none; + else if (rbFtEthernet2->isChecked()) + pStream->proto.ft = Stream::e_ft_eth_2; + else if (rbFt802Dot3Raw->isChecked()) + pStream->proto.ft = Stream::e_ft_802_3_raw; + else if (rbFt802Dot3Llc->isChecked()) + pStream->proto.ft = Stream::e_ft_802_3_llc; + else if (rbFtLlcSnap->isChecked()) + pStream->proto.ft = Stream::e_ft_snap; + + qDebug("store ft = %d\n", pStream->proto.ft); + + pStream->proto.dsap = leDsap->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->proto.ssap = leSsap->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->proto.ctl = leControl->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->proto.ouiMsb = leOui->text().remove(QChar(' ')).toULong(&isOk, 16) >> 16; + pStream->proto.ouiLshw = 0x0000FFFF & leOui->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->proto.etherType = leType->text().remove(QChar(' ')).toULong(&isOk, 16); + + pStream->proto.ipProto = leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16); + + // Just check for None/Other - no need to do anything for specific supported protocols + pStream->proto.protoMask = 0; + if (rbL3None->isChecked()) + pStream->proto.protoMask |= PM_L3_PROTO_NONE; + else if (rbL3Other->isChecked()) + pStream->proto.protoMask |= PM_L3_PROTO_OTHER; + + if (rbL4None->isChecked()) + pStream->proto.protoMask |= PM_L4_PROTO_NONE; + else if (rbL4Other->isChecked()) + pStream->proto.protoMask |= PM_L4_PROTO_OTHER; + } + + // L2 + { + // L2 | Ethernet + { + pStream->l2.eth.dstMacMshw = leDstMac->text().remove(QChar(' ')).left(4).toULong(&isOk, 16); + pStream->l2.eth.dstMacLsw = leDstMac->text().remove(QChar(' ')).right(8).toULong(&isOk, 16); + pStream->l2.eth.dstMacMode = (Stream::MacAddrMode) cmbDstMacMode->currentIndex(); + pStream->l2.eth.dstMacCount = leDstMacCount->text().toULong(&isOk); + pStream->l2.eth.dstMacStep = leDstMacStep->text().toULong(&isOk); + + pStream->l2.eth.srcMacMshw = leSrcMac->text().remove(QChar(' ')).left(4).toULong(&isOk, 16); + pStream->l2.eth.srcMacLsw = leSrcMac->text().remove(QChar(' ')).right(8).toULong(&isOk, 16); + pStream->l2.eth.srcMacMode = (Stream::MacAddrMode) cmbSrcMacMode->currentIndex(); + pStream->l2.eth.srcMacCount = leSrcMacCount->text().toULong(&isOk); + pStream->l2.eth.srcMacStep = leSrcMacStep->text().toULong(&isOk); + + pStream->l2.eth.vlanMask = 0; + + pStream->l2.eth.cvlanPrio = cmbCvlanPrio->currentIndex(); + pStream->l2.eth.cvlanCfi = cmbCvlanCfi->currentIndex(); + pStream->l2.eth.cvlanId = leCvlanId->text().toULong(&isOk); + pStream->l2.eth.ctpid = leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk); + if (cbCvlanTpidOverride->isChecked()) + pStream->l2.eth.vlanMask |= VM_CVLAN_TPID_OVERRIDE; + if (gbCvlan->isChecked()) + pStream->l2.eth.vlanMask |= VM_CVLAN_TAGGED; + + pStream->l2.eth.svlanPrio = cmbSvlanPrio->currentIndex(); + pStream->l2.eth.svlanCfi = cmbSvlanCfi->currentIndex(); + pStream->l2.eth.svlanId = leSvlanId->text().toULong(&isOk); + pStream->l2.eth.stpid = leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk); + if (cbSvlanTpidOverride->isChecked()) + pStream->l2.eth.vlanMask |= VM_SVLAN_TPID_OVERRIDE; + if (gbSvlan->isChecked()) + pStream->l2.eth.vlanMask |= VM_SVLAN_TAGGED; + } + } + + // L3 + { + // L3 | IP + { + pStream->l3.ip.ipMask = 0; + + pStream->l3.ip.ver = leIpVersion->text().toULong(&isOk); + if (cbIpVersionOverride->isChecked()) + pStream->l3.ip.ipMask |= IM_OVERRIDE_VERSION; + pStream->l3.ip.hdrLen = leIpHdrLen->text().toULong(&isOk); + if (cbIpHdrLenOverride->isChecked()) + pStream->l3.ip.ipMask |= IM_OVERRIDE_HDRLEN; + + pStream->l3.ip.tos = leIpTos->text().toULong(&isOk, 16); + + pStream->l3.ip.totLen = leIpLength->text().toULong(&isOk); + if (cbIpLengthOverride->isChecked()) + pStream->l3.ip.ipMask |= IM_OVERRIDE_TOTLEN; + + pStream->l3.ip.id = leIpId->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->l3.ip.fragOfs = leIpFragOfs->text().toULong(&isOk); + if (cbIpFlagsDf->isChecked()) pStream->l3.ip.ipMask |= IP_FLAG_DF; + if (cbIpFlagsMf->isChecked()) pStream->l3.ip.ipMask |= IP_FLAG_MF; + + pStream->l3.ip.ttl = leIpTtl->text().toULong(&isOk); + pStream->l3.ip.proto = leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16); + + pStream->l3.ip.cksum = leIpCksum->text().remove(QChar(' ')).toULong(&isOk); + if (cbIpCksumOverride->isChecked()) + pStream->l3.ip.ipMask |= IM_OVERRIDE_CKSUM; + + pStream->l3.ip.srcIp = QHostAddress(leIpSrcAddr->text()).toIPv4Address(); + pStream->l3.ip.srcIpMode = (Stream::IpAddrMode) cmbIpSrcAddrMode->currentIndex(); + pStream->l3.ip.srcIpCount = leIpSrcAddrCount->text().toULong(&isOk); + pStream->l3.ip.srcIpMask = QHostAddress(leIpSrcAddrMask->text()).toIPv4Address(); + + pStream->l3.ip.dstIp = QHostAddress(leIpDstAddr->text()).toIPv4Address(); + pStream->l3.ip.dstIpMode = (Stream::IpAddrMode) cmbIpDstAddrMode->currentIndex(); + pStream->l3.ip.dstIpCount = leIpDstAddrCount->text().toULong(&isOk); + pStream->l3.ip.dstIpMask = QHostAddress(leIpDstAddrMask->text()).toIPv4Address(); + } + + // L3 | ARP + { + // TODO + } + } + + // L4 + { + // L4 | TCP + { + pStream->l4.tcp.tcpMask = 0; + + pStream->l4.tcp.srcPort = leTcpSrcPort->text().toULong(&isOk); + pStream->l4.tcp.dstPort = leTcpDstPort->text().toULong(&isOk); + + pStream->l4.tcp.seqNum = leTcpSeqNum->text().toULong(&isOk); + pStream->l4.tcp.ackNum = leTcpAckNum->text().toULong(&isOk); + + pStream->l4.tcp.hdrLen = leTcpHdrLen->text().toULong(&isOk); + if (cbTcpHdrLenOverride->isChecked()) + pStream->l4.tcp.tcpMask |= TM_OVERRIDE_HDRLEN; + + pStream->l4.tcp.window = leTcpWindow->text().toULong(&isOk); + + pStream->l4.tcp.cksum = leTcpCksum->text().remove(QChar(' ')).toULong(&isOk); + if (cbTcpCksumOverride->isChecked()) + pStream->l4.tcp.tcpMask |= TM_OVERRIDE_CKSUM; + + pStream->l4.tcp.urgPtr = leTcpUrgentPointer->text().toULong(&isOk); + + pStream->l4.tcp.flags = 0; + if (cbTcpFlagsUrg->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_URG; + if (cbTcpFlagsAck->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_ACK; + if (cbTcpFlagsPsh->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_PSH; + if (cbTcpFlagsRst->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_RST; + if (cbTcpFlagsSyn->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_SYN; + if (cbTcpFlagsFin->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_FIN; + } + + // L4 | UDP + { + pStream->l4.udp.udpMask = 0; + + pStream->l4.udp.srcPort = leUdpSrcPort->text().toULong(&isOk); + pStream->l4.udp.dstPort = leUdpDstPort->text().toULong(&isOk); + + pStream->l4.udp.totLen = leUdpLength->text().toULong(&isOk); + if (cbUdpLengthOverride->isChecked()) pStream->l4.udp.udpMask |= UM_OVERRIDE_TOTLEN; + + pStream->l4.udp.cksum = leUdpCksum->text().remove(QChar(' ')).toULong(&isOk); + if (cbUdpCksumOverride->isChecked()) pStream->l4.udp.udpMask |= UM_OVERRIDE_CKSUM; + } + + // L4 | ICMP + { + // TODO + } + + // L4 | IGMP + { + // TODO + } + } +} +void StreamConfigDialog::on_pbOk_clicked() +{ + // Store dialog contents into stream + StoreCurrentStream(); + qDebug("stream stored"); +} + +//Junk Line for introducing a compiler error + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..95e7380 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,58 @@ +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 1522 + +/* +** TODO +** - Improve HexStr handling +** +*/ + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(QList *streamList, uint streamIndex, + QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + QList *mpStreamList; + uint mCurrentStreamIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbDstMacMode_currentIndexChanged(QString mode); + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_rbFtLlcSnap_toggled(bool checked); + + void on_rbL3Ipv4_toggled(bool checked); + void on_rbL3Arp_toggled(bool checked); + + void on_rbL4Icmp_toggled(bool checked); + void on_rbL4Igmp_toggled(bool checked); + void on_rbL4Tcp_toggled(bool checked); + void on_rbL4Udp_toggled(bool checked); + void on_rbL4Other_toggled(bool checked); + + void update_NumPacketsAndNumBursts(); + + void on_pbOk_clicked(); +}; + +QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets); + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..85f29e3 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,3224 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 554 + 521 + + + + Dialog + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Packet Config + + + + + + Qt::Horizontal + + + + 171 + 20 + + + + + + + + Data Pattern + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Frame Length (including CRC) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + 64 + + + 32767 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 64 + + + 32767 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + 64 + + + 32767 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 2 + + + + Protocols + + + + + + Frame Type + + + + + + + + + + None + + + false + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + true + + + + + + + LLC SNAP + + + + + + + + + Qt::Vertical + + + + + + + + + DSAP + + + + + + + true + + + HH; + + + + + + + SSAP + + + + + + + true + + + HH; + + + + + + + Control + + + + + + + true + + + HH; + + + + + + + OUI + + + + + + + true + + + HH HH HH; + + + + + + + Type + + + + + + + true + + + HH HH; + + + + + + + + + + + + + + L3 + + + + + + None + + + true + + + + + + + IPv4 + + + false + + + + + + + ARP + + + + + + + Other + + + + + + + + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + L2 + + + + + + Ethernet + + + + + + Destination + + + + + + + HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + Count + + + + + + + false + + + 1 + + + 1 + + + + + + + Step + + + + + + + false + + + 1 + + + 1 + + + + + + + Source + + + + + + + HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + Count + + + + + + + false + + + 1 + + + + + + + Step + + + + + + + false + + + 1 + + + 1 + + + + + + + + + + VLAN/CVLAN + + + true + + + false + + + + + + Priority + + + + + + + CFI + + + + + + + VLAN + + + + + + + false + + + Override TPID + + + + + + + false + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + false + + + + 0 + + + + + 1 + + + + + + + + false + + + 0 + + + + + + + false + + + HH HH; + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + SVLAN + + + true + + + false + + + + + + Priority + + + + + + + CFI + + + + + + + VLAN + + + + + + + false + + + Override TPID + + + + + + + false + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + false + + + + 0 + + + + + 1 + + + + + + + + false + + + 0 + + + + + + + false + + + HH HH; + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + L3 + + + + + + 0 + + + + + + + + + Override Version + + + + + + + false + + + 4 + + + + + + + Override Header Length + + + + + + + false + + + 5 + + + + + + + TOS/DSCP + + + + + + + HH; + + + + + + + + + + false + + + ... + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + HH HH; + + + + + + + + + Qt::Vertical + + + + + + + + + Fragment Offset + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + 64 + + + + + + + Protocol + + + + + + + false + + + + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + + + + + + false + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + + + + + + + + 255.255.255.255 + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + + + + + + + + 255.255.255.255 + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 470 + 16 + + + + + + + + + + + 105 + 100 + 296 + 76 + + + + ARP : TODO + + + Qt::AlignCenter + + + + + + + + + + L4 + + + + + + 1 + + + + + + + + + Source Port + + + + + + + + + + Destination Port + + + + + + + + + + Sequence Number + + + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + Urgent Pointer + + + + + + + + + + + + Qt::Vertical + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 181 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Source Port + + + + + + + + + + Destination Port + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + ICMP: TODO + + + + + + + + + + + IGMP: TODO + + + + + + + + + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + 1 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto Stream + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + Qt::Horizontal + + + + 41 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + leNumPackets + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bursts/Sec + + + leNumPackets + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Gaps + + + false + + + false + + + + + + + + + icons/gaps.png + + + + + + + ISG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leNumPackets + + + + + + + IBG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 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> + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + HexLineEdit + QLineEdit +
hexlineedit.h
+
+
+ + twTopLevel + cmbPatternMode + lePattern + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + twProto + leDstMac + cmbDstMacMode + leDstMacCount + leDstMacStep + leSrcMac + cmbSrcMacMode + leSrcMacCount + leSrcMacStep + gbCvlan + cmbCvlanPrio + cmbCvlanCfi + leCvlanId + cbCvlanTpidOverride + leCvlanTpid + gbSvlan + cmbSvlanPrio + cmbSvlanCfi + leSvlanId + cbSvlanTpidOverride + leSvlanTpid + + + + + + + cbCvlanTpidOverride + toggled(bool) + leCvlanTpid + setEnabled(bool) + + + 147 + 238 + + + 147 + 238 + + + + + cbSvlanTpidOverride + toggled(bool) + leSvlanTpid + setEnabled(bool) + + + 147 + 238 + + + 147 + 238 + + + + + gbCvlan + toggled(bool) + cmbCvlanPrio + setEnabled(bool) + + + 101 + 238 + + + 101 + 238 + + + + + gbCvlan + toggled(bool) + cmbCvlanCfi + setEnabled(bool) + + + 101 + 238 + + + 101 + 238 + + + + + gbCvlan + toggled(bool) + leCvlanId + setEnabled(bool) + + + 101 + 238 + + + 133 + 238 + + + + + gbCvlan + toggled(bool) + cbCvlanTpidOverride + setEnabled(bool) + + + 101 + 238 + + + 147 + 238 + + + + + gbSvlan + toggled(bool) + cmbSvlanPrio + setEnabled(bool) + + + 101 + 238 + + + 101 + 238 + + + + + gbSvlan + toggled(bool) + leSvlanId + setEnabled(bool) + + + 101 + 238 + + + 101 + 238 + + + + + gbSvlan + toggled(bool) + cbSvlanTpidOverride + setEnabled(bool) + + + 101 + 238 + + + 147 + 238 + + + + + gbSvlan + toggled(bool) + cmbSvlanCfi + setEnabled(bool) + + + 101 + 238 + + + 101 + 238 + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 79 + 247 + + + 99 + 247 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 79 + 247 + + + 99 + 247 + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 42 + 171 + + + 232 + 170 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 42 + 197 + + + 232 + 196 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 42 + 252 + + + 232 + 251 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 383 + 274 + + + 506 + 274 + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 79 + 247 + + + 99 + 247 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 79 + 247 + + + 99 + 247 + + + + + rbFtNone + toggled(bool) + lblDsap + setHidden(bool) + + + 43 + 186 + + + 137 + 185 + + + + + rbFtNone + toggled(bool) + leDsap + setHidden(bool) + + + 43 + 186 + + + 178 + 185 + + + + + rbFtNone + toggled(bool) + lblSsap + setHidden(bool) + + + 43 + 186 + + + 137 + 211 + + + + + rbFtNone + toggled(bool) + leSsap + setHidden(bool) + + + 43 + 186 + + + 178 + 211 + + + + + rbFtNone + toggled(bool) + lblControl + setHidden(bool) + + + 43 + 186 + + + 137 + 237 + + + + + rbFtNone + toggled(bool) + leControl + setHidden(bool) + + + 43 + 186 + + + 178 + 237 + + + + + rbFtNone + toggled(bool) + lblOui + setHidden(bool) + + + 43 + 186 + + + 137 + 263 + + + + + rbFtNone + toggled(bool) + leOui + setHidden(bool) + + + 43 + 186 + + + 178 + 263 + + + + + rbFtNone + toggled(bool) + lblTpe + setHidden(bool) + + + 43 + 186 + + + 137 + 289 + + + + + rbFtNone + toggled(bool) + leType + setHidden(bool) + + + 43 + 186 + + + 178 + 289 + + + + + rbFtEthernet2 + toggled(bool) + lblDsap + setHidden(bool) + + + 43 + 211 + + + 137 + 185 + + + + + rbFtEthernet2 + toggled(bool) + leDsap + setHidden(bool) + + + 43 + 211 + + + 178 + 185 + + + + + rbFtEthernet2 + toggled(bool) + lblSsap + setHidden(bool) + + + 43 + 211 + + + 137 + 211 + + + + + rbFtEthernet2 + toggled(bool) + leSsap + setHidden(bool) + + + 43 + 211 + + + 178 + 211 + + + + + rbFt802Dot3Raw + toggled(bool) + lblControl + setHidden(bool) + + + 43 + 236 + + + 137 + 237 + + + + + rbFt802Dot3Raw + toggled(bool) + leControl + setHidden(bool) + + + 43 + 236 + + + 178 + 237 + + + + + rbFtEthernet2 + toggled(bool) + lblControl + setHidden(bool) + + + 43 + 211 + + + 137 + 237 + + + + + rbFtEthernet2 + toggled(bool) + leControl + setHidden(bool) + + + 43 + 211 + + + 178 + 237 + + + + + rbFtEthernet2 + toggled(bool) + lblOui + setHidden(bool) + + + 43 + 211 + + + 137 + 263 + + + + + rbFtEthernet2 + toggled(bool) + leOui + setHidden(bool) + + + 43 + 211 + + + 178 + 263 + + + + + rbFt802Dot3Raw + toggled(bool) + lblDsap + setHidden(bool) + + + 43 + 236 + + + 137 + 185 + + + + + rbFt802Dot3Raw + toggled(bool) + leDsap + setHidden(bool) + + + 43 + 236 + + + 178 + 185 + + + + + rbFt802Dot3Raw + toggled(bool) + lblSsap + setHidden(bool) + + + 43 + 236 + + + 137 + 211 + + + + + rbFt802Dot3Raw + toggled(bool) + leSsap + setHidden(bool) + + + 43 + 236 + + + 178 + 211 + + + + + rbFt802Dot3Raw + toggled(bool) + lblControl + setHidden(bool) + + + 43 + 236 + + + 137 + 237 + + + + + rbFt802Dot3Raw + toggled(bool) + leControl + setHidden(bool) + + + 43 + 236 + + + 178 + 237 + + + + + rbFt802Dot3Raw + toggled(bool) + lblOui + setHidden(bool) + + + 43 + 236 + + + 137 + 263 + + + + + rbFt802Dot3Raw + toggled(bool) + leOui + setHidden(bool) + + + 43 + 236 + + + 178 + 263 + + + + + rbFt802Dot3Raw + toggled(bool) + lblTpe + setHidden(bool) + + + 43 + 236 + + + 137 + 289 + + + + + rbFt802Dot3Raw + toggled(bool) + leType + setHidden(bool) + + + 43 + 236 + + + 178 + 289 + + + + + pbOk + clicked() + StreamConfigDialog + accept() + + + 460 + 510 + + + 565 + 433 + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 543 + 510 + + + 561 + 466 + + + + + rbFtLlcSnap + toggled(bool) + leDsap + setDisabled(bool) + + + 43 + 286 + + + 178 + 185 + + + + + rbFtLlcSnap + toggled(bool) + leSsap + setDisabled(bool) + + + 43 + 286 + + + 178 + 211 + + + + + rbFtLlcSnap + toggled(bool) + leControl + setDisabled(bool) + + + 43 + 286 + + + 178 + 237 + + + + + rbL3Ipv4 + toggled(bool) + rbL4Tcp + setEnabled(bool) + + + 355 + 196 + + + 300 + 291 + + + + + rbL3Ipv4 + toggled(bool) + rbL4Udp + setEnabled(bool) + + + 355 + 196 + + + 372 + 291 + + + + + rbL3Ipv4 + toggled(bool) + rbL4Other + setEnabled(bool) + + + 355 + 196 + + + 443 + 291 + + + + + rbL3Ipv4 + toggled(bool) + rbL4Icmp + setEnabled(bool) + + + 355 + 196 + + + 372 + 267 + + + + + rbL3Ipv4 + toggled(bool) + rbL4Igmp + setEnabled(bool) + + + 355 + 196 + + + 443 + 267 + + + + + rbL3None + clicked() + rbL4None + click() + + + 300 + 196 + + + 300 + 267 + + + + + rbL3Arp + clicked() + rbL4None + click() + + + 407 + 196 + + + 300 + 267 + + + + + rbL3Other + clicked() + rbL4None + click() + + + 457 + 196 + + + 300 + 267 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 334 + 147 + + + 411 + 177 + + + + + rbSendPackets + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 38 + 73 + + + 180 + 284 + + + + + rbSendBursts + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 46 + 98 + + + 162 + 313 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 73 + 108 + + + 225 + 184 + + + + +
diff --git a/client/streamlistmodel.cpp b/client/streamlistmodel.cpp new file mode 100644 index 0000000..9812720 --- /dev/null +++ b/client/streamlistmodel.cpp @@ -0,0 +1,119 @@ +#include "streamlistmodel.h" + +StreamListModel::StreamListModel(QObject *parent) + : QAbstractTableModel(parent) +{ + uint i; + + // Enable all streams by default + for (i=0; i= MAX_ROWS) + return QVariant(); + + if (index.column() >= MAX_COLS) + return QVariant(); + + // Check role + if (index.column() == 0) // Icon + { + if ((role == Qt::DisplayRole)) + return QString("EDIT"); + else + return QVariant(); + } + else if (index.column() == 1) // Name + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return streamList[index.row()].streamName; + else + return QVariant(); + } + else if (index.column() == 2) // Enabled? + { + //if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) + // return streamList[index.row()].isEnabled ? Qt::Checked : Qt::Unchecked; + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return streamList[index.row()].isEnabled; + else + return QVariant(); + } +} + +bool StreamListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + if (index.column() == 1) // Name + streamList[index.row()].streamName = value.toString(); + else if (index.column() == 2) // Enabled? + streamList[index.row()].isEnabled = value.toBool(); + else + return false; + + return true; + } + return false; +} + +QVariant StreamListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return StreamListCols.at(section); + else + return QString("%1").arg(section+1); +} + +#if 0 +// QModelIndex StreamListModel::index (int portNum, PortStat stat, const QModelIndex & parent = QModelIndex() ) const + +void StreamListModel::on_portStatsUpdate(int port, void*stats) +{ + int i; + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + for (i = 0; i < e_STAT_MAX; i++) + dummyStats[port][i] = ((int *)stats)[i]; + + emit dataChanged(topLeft, bottomRight); +} + +#endif diff --git a/client/streamlistmodel.h b/client/streamlistmodel.h new file mode 100644 index 0000000..54c222b --- /dev/null +++ b/client/streamlistmodel.h @@ -0,0 +1,46 @@ +#ifndef _STREAM_LIST_MODEL_H +#define _STREAM_LIST_MODEL_H + +#include +#include + +static QStringList StreamListCols = (QStringList() + << "Icon" + << "Name" + << "Enable" +); + +#define MAX_ROWS 5 +#define MAX_COLS 3 + +class StreamListModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + //StreamListModel(QObject *parent = 0) : QAbstractTableModel(parent) {} + StreamListModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + // QModelIndex index (int portNum, PortStat stat, const QModelIndex & parent = QModelIndex() ) const; + + public slots: +// void on_portStatsUpdate(int port, void*stats); + + private: + // FIXME: temp +//#define NUM_PORTS 2 +// int dummyStats[NUM_PORTS][e_STAT_MAX]; + struct { + QString streamName; + bool isEnabled; + } streamList[MAX_ROWS]; + +}; + +#endif diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..21588b7 --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,218 @@ +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->mStreams.size(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &parent ) const +{ + return (int) StreamMaxFields; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable; + break; + default: + break; + } + + return flags; +} +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->mStreams.size()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); +#if 0 + else if ((role == Qt::DisplayRole)) + return QString("EDIT"); +#endif + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->mStreams[index.row()].name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + #if 0 + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return streamList[index.row()].isEnabled; + if ((role == Qt::DisplayRole) || (role == Qt::CheckStateRole) || + #endif + if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) + { + if (mCurrentPort->mStreams[index.row()].isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid() && role == Qt::EditRole) + { + // Edit Supported Fields + switch (index.column()) + { + case StreamName: + mCurrentPort->mStreams[index.row()].setName(value.toString()); + return true; + break; + case StreamStatus: + mCurrentPort->mStreams[index.row()].setEnabled(value.toBool()); + return true; + break; + + // Edit Not Supported Fields + case StreamIcon: + return false; + break; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString("Icon"); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString("Enabled"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->mStreams.insert(row, Stream()); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + // FIXME(HIGH): do we need to free the removed stream? + mCurrentPort->mStreams.removeAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + quint16 pg = current.internalId() >> 16; + mCurrentPort = &pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + } + reset(); +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..de648a2 --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,49 @@ +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + + StreamMaxFields + }; + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + +}; + +#endif diff --git a/common/protocol.h b/common/protocol.h new file mode 100644 index 0000000..f7c61dc --- /dev/null +++ b/common/protocol.h @@ -0,0 +1,53 @@ +#ifndef _PROTOCOL_H +#define _PROTOCOL_H + +#define UINT8 unsigned char +#define UINT16 unsigned short +#define UINT32 unsigned int + +#define BYTESWAP4(x) \ + (((x & 0xFF000000) >> 24) | \ + ((x & 0x00FF0000) >> 8) | \ + ((x & 0x0000FF00) << 8) | \ + ((x & 0x000000FF) << 24)) + +#define BYTESWAP2(x) \ + (((x & 0xFF00) >> 8) | \ + ((x & 0x00FF) << 8)) + +// TODO: portability +#define HTONL(x) BYTESWAP4(x) +#define NTOHL(x) BYTESWAP4(x) +#define HTONS(x) BYTESWAP2(x) +#define NTOHS(x) BYTESWAP2(x) + + +typedef struct { + UINT8 ver; + UINT8 resv1; + UINT16 resv2; + UINT16 msgType; + UINT16 msgLen; +} tCommHdr; + +typedef enum { + e_MT_CapabilityReq=1, + e_MT_CapabilityInfo +} eMsgType; + +typedef enum { + e_TT_PortCapability +} eTlvType; + +typedef struct { + UINT16 tlvType; + UINT16 tlvLen; + UINT32 port; + UINT32 speed; +#define TLV_MAX_PORT_NAME 64 +#define TLV_MAX_PORT_DESC 64 + char name[TLV_MAX_PORT_NAME]; + char desc[TLV_MAX_PORT_DESC]; +} tTlvPortCapability; + +#endif diff --git a/server/abstracthost.h b/server/abstracthost.h new file mode 100644 index 0000000..38e266c --- /dev/null +++ b/server/abstracthost.h @@ -0,0 +1,12 @@ +#ifndef _ABSTRACT_HOST +#define _ABSTRACT_HOST + +class AbstractHost +{ + public: + virtual void Log(const char* str) = 0; + virtual int SendMsg(const void* msg, int size) = 0; +}; + +#endif + diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..54bf9cf --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,85 @@ +#include "drone.h" + +extern int myport; // FIXME(HIGH) + +Drone::Drone(QDialog *parent) + : QDialog(parent) +{ + ui.setupUi(this); + serverPortNum = DRONE_PORT; + clientSock = NULL; + + if (myport) + serverPortNum = myport; + + rxtx = new RxTx(this); + + server = new QTcpServer(this); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + //if (!server->listen(QHostAddress("10.0.0.1"), serverPortNum)) + if (!server->listen(QHostAddress::Any, serverPortNum)) + LogInt(tr("Unable to start the server: %1").arg(server->errorString())); + else + LogInt(tr("The server is running on %1:%2").arg(server->serverAddress().toString()).arg(server->serverPort())); +} + +void Drone::Log(const char* str) +{ + ui.teLog->append(QString(str)); +} + +int Drone::SendMsg(const void* msg, int size) +{ + qDebug("Inside SendMsg\n"); + clientSock->write((char*) msg, size); +} + +void Drone::LogInt(const QString &str) +{ + ui.teLog->append(str); +} + +void Drone::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + LogInt(tr("already connected, no new connections will be accepted\n")); + sock = server->nextPendingConnection(); + // TODO: Send reason msg to client + sock->disconnectFromHost(); + goto _exit; + } + clientSock = server->nextPendingConnection(); + LogInt(tr("accepting new connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + + +_exit: + return; +} + +void Drone::when_disconnected() +{ + LogInt(tr("closing connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + clientSock->deleteLater(); + clientSock = NULL; +} + +void Drone::when_dataAvail() +{ + QByteArray msg = clientSock->read(1024); // FIXME: hardcoding + LogInt(QString(msg.toHex())); + rxtx->ProcessMsg(msg.constData(), msg.size()); +} + +void Drone::when_error(QAbstractSocket::SocketError socketError) +{ + LogInt(clientSock->errorString()); +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..f99255b --- /dev/null +++ b/server/drone.h @@ -0,0 +1,36 @@ +#ifndef _DRONE_H +#define _DRONE_H + +#include +#include +#include "ui_drone.h" +#include "abstracthost.h" +#include "rxtx.h" + + +class Drone : public QDialog, AbstractHost +{ + Q_OBJECT + + public: + Drone(QDialog *parent = 0); + void Log(const char *msg); + int SendMsg(const void* msg, int msgLen); + + Ui::Drone ui; + private: + RxTx *rxtx; + QTcpServer *server; + QTcpSocket *clientSock; +#define DRONE_PORT 7878 + quint16 serverPortNum; + // Ui::Drone ui; + void LogInt(const QString &msg); + private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; +#endif + diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..1fd8862 --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += qt +QT += network +DEFINES += HAVE_REMOTE +INCLUDEPATH += "C:\DevelLibs\WpdPack\Include" +LIBS += -L"C:\DevelLibs\WpdPack\Lib" -lwpcap +HEADERS += drone.h +FORMS += drone.ui +SOURCES += drone_main.cpp drone.cpp rxtx.cpp diff --git a/server/drone.ui b/server/drone.ui new file mode 100644 index 0000000..5caa184 --- /dev/null +++ b/server/drone.ui @@ -0,0 +1,126 @@ + + Drone + + + + 0 + 0 + 400 + 300 + + + + Drone + + + + + + 1 + + + + Status + + + + + 160 + 100 + 46 + 14 + + + + TODO + + + + + + Log + + + + + + true + + + false + + + + + + + + + + + + + Clear Log + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Exit + + + + + + + + + + + pushButton + clicked(bool) + Drone + accept() + + + 341 + 279 + + + 226 + 268 + + + + + pbClearLog + clicked() + teLog + clear() + + + 52 + 278 + + + 100 + 185 + + + + + diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..5a66b6a --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,22 @@ +#include "drone.h" + +Drone *drone; + +//void FindDevList(void); + +int myport; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // FIXME(HIGH) + if (argc > 1) + myport = atoi(argv[1]); + + drone = new Drone; + //FindDevList(); + drone->show(); + return app.exec(); +} + diff --git a/server/rxtx.cpp b/server/rxtx.cpp new file mode 100644 index 0000000..7c0be69 --- /dev/null +++ b/server/rxtx.cpp @@ -0,0 +1,147 @@ +#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__)) +#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} + + +RxTx::RxTx(AbstractHost *host) +{ + pcap_if_t *d; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + + // Init Data + RxTx::host = host; + numPorts = 0; + alldevs = NULL; + + LOG("Retrieving the device list from the local machine\n"); + if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) + { + LOG("Error in pcap_findalldevs_ex: %s\n", errbuf); + goto _fail; + } + + /* Count number of local ports */ + for(d = alldevs; d != NULL; d = d->next) + numPorts++; + + portInfo = new PortInfo[numPorts]; + + /* Print the list */ + for(i=0, d=alldevs; d!=NULL; i++, d=d->next) + { + portInfo[i].portId = i; + portInfo[i].dev = d; +#if 1 + LOG("%d. %s", i, d->name); + if (d->description) + { + LOG(" (%s)\n", d->description); + } + else + LOG(" (No description available)\n"); +#endif + } + + if (i == 0) + { + LOG("\nNo interfaces found! Make sure WinPcap is installed.\n"); + goto _fail; + } + +_fail: + return; +} + +#if 0 +RxTx::LOG(char* fmt, ...) +{ + sprintf(logStr, fmt, _VA_ARGS_); + host->LOG(logStr); +} +#endif + +RxTx::~RxTx() +{ + pcap_freealldevs(alldevs); +} + +void RxTx::ProcessMsg(const char* msg, int len) +{ + tCommHdr *hdr; + // TODO: For now, assuming we'll get a complete msg + // but need to fix this as this is a TCP stream + + hdr = (tCommHdr*) msg; + + if (hdr->ver != 1) // FIXME:hardcoding + { + LOG("Rcvd msg with invalid version %d\n", hdr->ver); + goto _exit; + } + + qDebug("msgType - %x: %x\n", hdr->msgType, NTOHS(hdr->msgType)); + switch (NTOHS(hdr->msgType)) + { + case e_MT_CapabilityReq: + SendCapabilityInfo(); + break; + + default: + LOG("Rcvd msg with unrecognized msgType %d\n", NTOHS(hdr->msgType)); + } + +_exit: + return; +} + +void RxTx::SendCapabilityInfo(void) +{ + unsigned char *msg, *p; + unsigned int i, msgLen; + + p = msg = (unsigned char*) pktBuff; + ((tCommHdr*)(p))->ver = 1; + ((tCommHdr*)(p))->msgType = HTONS(e_MT_CapabilityInfo); + p += sizeof(tCommHdr); + + for (i = 0; i < numPorts; i++) + { + // 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 +#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"); + //strcat(((tTlvPortCapability*)(p))->name, itoa(portInfo[i].portId, NULL, 10)); + itoa(portInfo[i].portId, &(((tTlvPortCapability*)(p))->name[3]), 10); +#endif + strncpy(((tTlvPortCapability*)(p))->desc, + portInfo[i].dev->description, TLV_MAX_PORT_DESC); + ((tTlvPortCapability*)(p))->desc[TLV_MAX_PORT_DESC -1] = 0; + p += sizeof(tTlvPortCapability); + } + msgLen = (p - msg); + ((tCommHdr*)(msg))->msgLen = HTONS(msgLen); + + logStr[0] = 0; + for (i = 0; i < msgLen >> 2; i++) + { + char word[10]; + + sprintf(word, "%08X ", HTONL(((unsigned int *)(msg))[i])); + strcat(logStr, word); + } + host->Log("Sending msg\n"); + host->Log(logStr); + host->SendMsg(pktBuff, msgLen); +} diff --git a/server/rxtx.h b/server/rxtx.h new file mode 100644 index 0000000..97eb2c3 --- /dev/null +++ b/server/rxtx.h @@ -0,0 +1,35 @@ +#ifndef _RXTX_H +#define _RXTX_H + +#include "pcap.h" +#include "abstracthost.h" + + +typedef struct +{ + unsigned int portId; + pcap_if_t *dev; +} PortInfo; + +class RxTx +{ + public: + RxTx(AbstractHost* host); + ~RxTx(); + void ProcessMsg(const char* msg, int len); + + private: + AbstractHost *host; + char logStr[1024]; + +#define MAX_PKT_SIZE 1024 + unsigned char pktBuff[MAX_PKT_SIZE]; + unsigned numPorts; + PortInfo *portInfo; + pcap_if_t *alldevs; + + //void Log(char *fmt, ...); + void SendCapabilityInfo(void); +}; + +#endif From 8251383351b6bfd0905f19d6ed1c4b7a198a8559 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 25 May 2008 11:30:30 +0000 Subject: [PATCH 002/294] - added PortStatsFilter - added PacketDump (test code) - added PacketTree (not complete) - Protocol stuff (not complete) --- client/dumpview.cpp | 175 +++++++++++ client/dumpview.h | 34 +++ client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/ostinato.pro | 7 + client/ostinato.qrc | 1 + client/packetmodel.cpp | 440 +++++++++++++++++++++++++++ client/packetmodel.h | 52 ++++ client/port.cpp | 2 + client/portgroup.cpp | 8 +- client/portstatsfilter.ui | 32 +- client/portstatsfilterdialog.cpp | 81 ++++- client/portstatsfilterdialog.h | 20 +- client/portstatswindow.cpp | 26 +- client/portstatswindow.h | 3 + client/portstatswindow.ui | 325 ++++++++++++-------- client/stream.h | 9 + client/streamconfigdialog.cpp | 11 + client/streamconfigdialog.h | 6 +- client/streamconfigdialog.ui | 490 ++++++++++++++---------------- common/protocol.h | 130 +++++++- server/rxtx.cpp | 379 ++++++++++++++++++++++- server/rxtx.h | 41 +++ 22 files changed, 1832 insertions(+), 440 deletions(-) create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/icons/portstats_filter.png create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h 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 0000000000000000000000000000000000000000..46060872cc1e32d6dc557e50d53a3d395e9916ea GIT binary patch literal 432 zcmV;h0Z;ykP)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Riicons/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 From 03ab9c4349651d251942c49ad718129cab190fc8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 25 May 2008 11:53:49 +0000 Subject: [PATCH 003/294] - Fixed some trivial compilation problems with Drone - Putting in the new PacketTreeView code (still not complete) --- client/packetmodel.cpp | 652 ++++++++++++++++++++--------------------- client/packetmodel.h | 88 ++++-- server/rxtx.cpp | 6 +- 3 files changed, 392 insertions(+), 354 deletions(-) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 4cbc92c..fd720dc 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,158 +1,45 @@ #include "packetmodel.h" - PacketModel::PacketModel(Stream *pStream, QObject *parent) { mpStream = pStream; + populatePacketProtocols(); + registerFrameTypeProto(); + registerVlanProto(); + registerIpProto(); + registerArpProto(); + registerTcpProto(); + registerUdpProto(); + registerIcmpProto(); + registerIgmpProto(); + registerData(); + registerInvalidProto(); } int PacketModel::rowCount(const QModelIndex &parent) const { - // Parent - Invisible Root. - // Children - Top Level Items + IndexId parentId; + + // Parent - Invalid i.e. Invisible Root. + // Children - Protocol (Top Level) Items if (!parent.isValid()) + return mPacketProtocols.count(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) { - 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 + case ITYP_PROTOCOL: + return fieldCount(parentId.ws.protocol); + case ITYP_FIELD: + return subfieldCount(parentId.ws.protocol, parentId.ws.field); + case ITYP_SUBFIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); } - // 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__); + qWarning("%s: Catch all - need to investigate", __FUNCTION__); return 0; // catch all } @@ -165,68 +52,42 @@ QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) cons { QModelIndex index; IndexId id, parentId; - uint vlanMask = mpStream->l2.eth.vlanMask; if (!hasIndex(row, col, parent)) goto _exit; + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = mPacketProtocols.at(row); + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item parentId.w = parent.internalId(); - - // Parent - Invisible Root - // Requested child - First/Top Level Item - if (parentId.ws.b2 == 0xFF) + id.w = parentId.w; + switch(parentId.ws.type) { - 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; + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + id.ws.field = row; 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); + case ITYP_FIELD: + // TODO(MED): Nothing till subfield support is added goto _exit; - } - // Parent - Second Level Item (Field) - // Requested child - Third Level Item (Subfield) - // TODO(MED): Support subfields - // Till then we return an invalid index + case ITYP_SUBFIELD: + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } _exit: return index; @@ -234,207 +95,342 @@ _exit: QModelIndex PacketModel::parent(const QModelIndex &index) const { + QModelIndex parentIndex; IndexId id, parentId; + if (!index.isValid()) + return QModelIndex(); + 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) + parentId.w = id.w; + switch(id.ws.type) { - uint vlanMask = mpStream->l2.eth.vlanMask; - int row = -1; + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; - parentId.ws.b2 = 0xFF; + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentId.ws.field = 0; + parentIndex = createIndex(mPacketProtocols.indexOf(parentId.ws.protocol), 0, + parentId.w); + goto _exit; - 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__); + case ITYP_SUBFIELD: + // TODO(MED): invalid index till subfield support is added + goto _exit; - return createIndex(row, 0, parentId.w); + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); } - // Third Level Item - Subfield - // Requested Parent => 2nd Level Item (Field) - // TODO(Med) - qWarning("%s: Unexpected leg", __FUNCTION__); - return QModelIndex(); +_exit: + return parentIndex; } QVariant PacketModel::data(const QModelIndex &index, int role) const { - IndexId id; + IndexId id; + ProtocolInfo proto; + + if (!index.isValid()) + return QVariant(); + + if (role != Qt::DisplayRole) + return QVariant(); id.w = index.internalId(); + foreach(proto, mProtocols) + { + if (proto.handle == id.ws.protocol) + goto _found; + } + return QVariant(); - if (id.ws.b2 == 0xFF) - return QString("Protocol Header"); - else - return QString("Field: Value"); +_found: + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return proto.name; + + case ITYP_FIELD: + return proto.fieldList.at(id.ws.field).name; + + case ITYP_SUBFIELD: + return QVariant(); // TODO(MED): Till subfield support is added + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + return QVariant(); } /* ** --------------- Private Stuff ----------------- */ -typedef union +void PacketModel::populatePacketProtocols() { - quint32 w; - struct + int proto; + + // Clear the protocols list + mPacketProtocols.clear(); + + // Check and populate L2 Protocol + switch(mpStream->proto.ft) { - quint8 b1; - quint8 b2; - quint8 b3; - quint8 b4; - } ws; -} IndexId; + case Stream::e_ft_none: + proto = PTYP_L2_NONE; + break; -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; + case Stream::e_ft_eth_2: + proto = PTYP_L2_ETH_2; + break; + + case Stream::e_ft_802_3_raw: + proto = PTYP_L2_802_3_RAW; + break; + + case Stream::e_ft_802_3_llc: + proto = PTYP_L2_802_3_LLC; + break; + + case Stream::e_ft_snap: + proto = PTYP_L2_SNAP; + break; + + default: + qDebug("%s: Unsupported frametype", __FUNCTION__); + proto = PTYP_INVALID; + } + mPacketProtocols.append(proto); + + // Check and populate VLANs, if present + if (mpStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) + mPacketProtocols.append(PTYP_SVLAN); + + if (mpStream->l2.eth.vlanMask & VM_CVLAN_TAGGED) + mPacketProtocols.append(PTYP_CVLAN); + + // Check and populate L3 protocols + if (mpStream->proto.protoMask & PM_L3_PROTO_NONE) + goto _data; + + switch(mpStream->proto.etherType) + { + case ETH_TYP_IP: + proto = PTYP_L3_IP; + break; + + case ETH_TYP_ARP: + proto = PTYP_L3_ARP; + break; + + default: + qDebug("%s: Unsupported ethtype", __FUNCTION__); + proto = PTYP_INVALID; + } + mPacketProtocols.append(proto); + + if (mpStream->proto.protoMask & PM_L4_PROTO_NONE) + goto _data; + + switch(mpStream->proto.ipProto) + { + case IP_PROTO_TCP: + proto = PTYP_L4_TCP; + break; + case IP_PROTO_UDP: + proto = PTYP_L4_UDP; + break; + case IP_PROTO_ICMP: + proto = PTYP_L4_ICMP; + break; + case IP_PROTO_IGMP: + proto = PTYP_L4_IGMP; + break; + default: + qDebug("%s: Unsupported ipProto", __FUNCTION__); + proto = PTYP_INVALID; + }; + mPacketProtocols.append(proto); + +_data: + mPacketProtocols.append(PTYP_DATA); } -bool PacketModel::isIndexL2Container(const QModelIndex& index) const +int PacketModel::fieldCount(uint protocol) const { - return isIndexContainer(index, 2); + ProtocolInfo proto; + + foreach(proto, mProtocols) + { + if (proto.handle == protocol) + { + qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); + qDebug("fieldcount = %d", proto.fieldList.size()); + return proto.fieldList.size(); + } + } + + return 0; } -bool PacketModel::isIndexSvlanContainer(const QModelIndex& index) const +int PacketModel::subfieldCount(uint protocol, int field) const { - return isIndexContainer(index, 0x88); + // TODO(MED): Till subfield support is added + return 0; } -bool PacketModel::isIndexCvlanContainer(const QModelIndex& index) const +/* +** ------------- Registration Functions --------------- +*/ + +void PacketModel::registerProto(uint handle, char *name, char *abbr) { - return isIndexContainer(index, 0x81); + ProtocolInfo proto; + + proto.handle = handle; + proto.name = QString(name); + proto.abbr = QString(abbr); + mProtocols.append(proto); } -bool PacketModel::isIndexL3Container(const QModelIndex& index) const +void PacketModel::registerField(uint protoHandle, char *name, char *abbr) { - return isIndexContainer(index, 3); + for (int i = 0; i < mProtocols.size(); i++) + { + if (mProtocols.at(i).handle == protoHandle) + { + FieldInfo field; + + field.name = QString(name); + field.abbr = QString(abbr); + mProtocols[i].fieldList.append(field); + qDebug("proto = %d, name = %s", protoHandle, name); + break; + } + } } -bool PacketModel::isIndexL4Container(const QModelIndex& index) const +void PacketModel::registerFrameTypeProto() { - return isIndexContainer(index, 4); + registerProto(PTYP_L2_NONE, "None", ""); + registerField(PTYP_L2_NONE, "Destination Mac", "dstMac"); + registerField(PTYP_L2_NONE, "Source Mac", "srcMac"); + + registerProto(PTYP_L2_ETH_2, "Ethernet II", "eth"); + registerField(PTYP_L2_ETH_2, "Destination Mac", "dstMac"); + registerField(PTYP_L2_ETH_2, "Source Mac", "srcMac"); + registerField(PTYP_L2_ETH_2, "Ethernet Type", "type"); + + registerProto(PTYP_L2_802_3_RAW, "IEEE 802.3 Raw", "dot3raw"); + registerField(PTYP_L2_802_3_RAW, "Destination Mac", "dstMac"); + registerField(PTYP_L2_802_3_RAW, "Source Mac", "srcMac"); + registerField(PTYP_L2_802_3_RAW, "Length", "len"); + + registerProto(PTYP_L2_802_3_LLC, "802.3 LLC", "dot3llc"); + registerField(PTYP_L2_802_3_LLC, "Destination Mac", "dstMac"); + registerField(PTYP_L2_802_3_LLC, "Source Mac", "srcMac"); + registerField(PTYP_L2_802_3_LLC, "Destination Service Acces Point", "dsap"); + registerField(PTYP_L2_802_3_LLC, "Source Service Acces Point", "ssap"); + registerField(PTYP_L2_802_3_LLC, "Control", "ctl"); + + registerProto(PTYP_L2_SNAP, "802.3 LLC SNAP", "dot3snap"); + registerField(PTYP_L2_SNAP, "Destination Mac", "dstMac"); + registerField(PTYP_L2_SNAP, "Source Mac", "srcMac"); + registerField(PTYP_L2_SNAP, "Destination Service Acces Point", "dsap"); + registerField(PTYP_L2_SNAP, "Source Service Access Point", "ssap"); + registerField(PTYP_L2_SNAP, "Control", "ctl"); + registerField(PTYP_L2_SNAP, "Organisationally Unique Identifier", "oui"); + registerField(PTYP_L2_SNAP, "Type", "type"); + } -bool PacketModel::isIndexField(const QModelIndex& index, int level) const +void PacketModel::registerVlanProto() { - IndexId id; - - id.w = index.internalId(); - if ((id.ws.b1 == level) && (id.ws.b2 != 0xFF) && (id.ws.b3 == 0xFF)) - return true; - else - return false; + registerProto(PTYP_SVLAN, "IEEE 802.1ad Service VLAN", "SVLAN"); + + registerField(PTYP_SVLAN, "Tag Protocol Identifier", "tpid"); + registerField(PTYP_SVLAN, "Priority Code Point", "pcp"); + registerField(PTYP_SVLAN, "Drop Eligible", "de"); + registerField(PTYP_SVLAN, "VLAN Identifier", "vlan"); + + registerProto(PTYP_CVLAN, "IEEE 802.1Q VLAN/CVLAN", "VLAN"); + + registerField(PTYP_CVLAN, "Tag Protocol Identifier", "tpid"); + registerField(PTYP_CVLAN, "Priority", "prio"); + registerField(PTYP_CVLAN, "Canonical Format Indicator", "cfi"); + registerField(PTYP_CVLAN, "VLAN Identifier", "vlan"); } -bool PacketModel::isIndexL2Field(const QModelIndex& index) const +void PacketModel::registerIpProto() { - return isIndexField(index, 2); + registerProto(PTYP_L3_IP, "Internet Protocol version 4", "IPv4"); + + registerField(PTYP_L3_IP, "Version", "ver"); + registerField(PTYP_L3_IP, "Header Length", "hdrlen"); + registerField(PTYP_L3_IP, "Type of Service/DiffServ Code Point", "tos"); + registerField(PTYP_L3_IP, "Total Length", "len"); + registerField(PTYP_L3_IP, "Identification", "id"); + registerField(PTYP_L3_IP, "Flags", "flags"); + registerField(PTYP_L3_IP, "Fragment Offset", "fragofs"); + registerField(PTYP_L3_IP, "Time to Live", "ttl"); + registerField(PTYP_L3_IP, "Protocol Id", "proto"); + registerField(PTYP_L3_IP, "Checksum", "cksum"); + registerField(PTYP_L3_IP, "Source IP", "srcip"); + registerField(PTYP_L3_IP, "Destination IP", "dstip"); } -bool PacketModel::isIndexL3Field(const QModelIndex& index) const +void PacketModel::registerArpProto() { - return isIndexField(index, 3); + // TODO (LOW) } -bool PacketModel::isIndexL4Field(const QModelIndex& index) const +void PacketModel::registerTcpProto() { - return isIndexField(index, 4); + registerProto(PTYP_L4_TCP, "Transmission Control Protocol", "TCP"); + + registerField(PTYP_L4_TCP, "Source Port", "srcport"); + registerField(PTYP_L4_TCP, "Destination Port", "dstport"); + registerField(PTYP_L4_TCP, "Sequence Number", "seqnum"); + registerField(PTYP_L4_TCP, "Acknowledgement Number", "acknum"); + registerField(PTYP_L4_TCP, "Header Length", "hdrlen"); + registerField(PTYP_L4_TCP, "Reserved", "rsvd"); + registerField(PTYP_L4_TCP, "Flags", "flags"); + registerField(PTYP_L4_TCP, "Window", "win"); + registerField(PTYP_L4_TCP, "Checksum", "cksum"); + registerField(PTYP_L4_TCP, "Urgent Pointer", "urgptr"); } -bool PacketModel::isIndexIpField(const QModelIndex& index) const +void PacketModel::registerUdpProto() { - IndexId id; - - id.w = index.internalId(); - if ((id.ws.b1 == 3) && (id.ws.b2 == 1) && (id.ws.b3 == 0xFF)) - return true; - else - return false; + registerProto(PTYP_L4_UDP, "User Datagram Protocol", "UDP"); + + registerField(PTYP_L4_UDP, "Source Port", "srcport"); + registerField(PTYP_L4_UDP, "Destination Port", "dstport"); + registerField(PTYP_L4_UDP, "Length", "len"); + registerField(PTYP_L4_UDP, "Checksum", "cksum"); } -bool PacketModel::isIndexArpField(const QModelIndex& index) const +void PacketModel::registerIcmpProto() { - IndexId id; - - id.w = index.internalId(); - if ((id.ws.b1 == 3) && (id.ws.b2 == 2) && (id.ws.b3 == 0xFF)) - return true; - else - return false; + // TODO (LOW) } -bool PacketModel::isIndexL4ProtoField(const QModelIndex& index, int proto) const +void PacketModel::registerIgmpProto() { - IndexId id; - - id.w = index.internalId(); - if ((id.ws.b1 == 4) && (id.ws.b2 == proto) && (id.ws.b3 == 0xFF)) - return true; - else - return false; + // TODO (LOW) } -bool PacketModel::isIndexTcpField(const QModelIndex& index) const +void PacketModel::registerInvalidProto() { - return isIndexL4ProtoField(index, IP_PROTO_TCP); + registerProto(PTYP_INVALID, "Invalid Protocol (bug in code)", "invalid"); } -bool PacketModel::isIndexUdpField(const QModelIndex& index) const +void PacketModel::registerData() { - return isIndexL4ProtoField(index, IP_PROTO_UDP); + registerProto(PTYP_DATA, "Data", "data"); } -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 index 1ecd0d2..6b2e82f 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -13,40 +13,84 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const { return QVariant(); } ; 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 + quint8 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 +#define ITYP_SUBFIELD 3 + quint8 protocol; + quint8 field; + quint8 subfield; } 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; + typedef struct + { + QString name; + QString abbr; + } FieldInfo; + + typedef struct + { + uint handle; + QString name; + QString abbr; + QList fieldList; + } ProtocolInfo; + + Stream *mpStream; + QList mPacketProtocols; + QList mProtocols; + + void registerProto(uint handle, char *name, char *abbr); + void registerField(uint protoHandle, char *name, char *abbr); + + void registerFrameTypeProto(); + void registerVlanProto(); + void registerIpProto(); + void registerArpProto(); + void registerTcpProto(); + void registerUdpProto(); + void registerIcmpProto(); + void registerIgmpProto(); + void registerData(); + void registerInvalidProto(); + + void populatePacketProtocols(); + int fieldCount(uint protocol) const; + int subfieldCount(uint protocol, int field) const; + +// FIXME(HIGH): Is this how I want this? +#define PTYP_L2_NONE 1 +#define PTYP_L2_ETH_2 2 +#define PTYP_L2_802_3_RAW 3 +#define PTYP_L2_802_3_LLC 4 +#define PTYP_L2_SNAP 5 + +#define PTYP_SVLAN 10 +#define PTYP_CVLAN 11 + +#define PTYP_L3_IP 30 +#define PTYP_L3_ARP 31 + +#define PTYP_L4_TCP 40 +#define PTYP_L4_UDP 41 +#define PTYP_L4_ICMP 42 +#define PTYP_L4_IGMP 43 + +#define PTYP_INVALID 0 +#define PTYP_DATA 0xFF + }; #endif diff --git a/server/rxtx.cpp b/server/rxtx.cpp index 0a1bcdf..2eab59b 100644 --- a/server/rxtx.cpp +++ b/server/rxtx.cpp @@ -102,7 +102,7 @@ void RxTx::ProcessMsg(const char* msg, int len) ProcessPortConfig(msg+sizeof(tCommHdr), len - sizeof(tCommHdr)); break; case e_MT_GetPortConfig: - SendPortInfo(); + SendPortInfo(0); // FIXME break; default: @@ -300,9 +300,7 @@ _exit: void RxTx::SendPortInfo(unsigned int port) { - // Assumption: port is valid - assert(port < numPorts); - + // FIXME } /* From a480be5a230756210a1cabb4a4e89840995ab4b7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2008 06:35:17 +0000 Subject: [PATCH 004/294] Finished basic PacketModel to feed actual data into PacketView TODO: PacketModel to feed data into DumpView --- client/packetmodel.cpp | 537 +++++++++++++++++++++++++++++++++++++---- client/packetmodel.h | 35 ++- 2 files changed, 519 insertions(+), 53 deletions(-) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index fd720dc..9064b1d 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,9 +1,11 @@ +#include #include "packetmodel.h" PacketModel::PacketModel(Stream *pStream, QObject *parent) { mpStream = pStream; populatePacketProtocols(); +#if 1 registerFrameTypeProto(); registerVlanProto(); registerIpProto(); @@ -14,6 +16,7 @@ PacketModel::PacketModel(Stream *pStream, QObject *parent) registerIgmpProto(); registerData(); registerInvalidProto(); +#endif } int PacketModel::rowCount(const QModelIndex &parent) const @@ -23,7 +26,7 @@ int PacketModel::rowCount(const QModelIndex &parent) const // Parent - Invalid i.e. Invisible Root. // Children - Protocol (Top Level) Items if (!parent.isValid()) - return mPacketProtocols.count(); + return protoCount(); // Parent - Valid Item parentId.w = parent.internalId(); @@ -32,13 +35,12 @@ int PacketModel::rowCount(const QModelIndex &parent) const case ITYP_PROTOCOL: return fieldCount(parentId.ws.protocol); case ITYP_FIELD: - return subfieldCount(parentId.ws.protocol, parentId.ws.field); - case ITYP_SUBFIELD: return 0; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } + Q_ASSERT(1 == 1); // Unreachable code qWarning("%s: Catch all - need to investigate", __FUNCTION__); return 0; // catch all } @@ -62,7 +64,7 @@ QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) cons { id.w = 0; id.ws.type = ITYP_PROTOCOL; - id.ws.protocol = mPacketProtocols.at(row); + id.ws.protocol = row; index = createIndex(row, col, id.w); goto _exit; } @@ -74,21 +76,18 @@ QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) cons { case ITYP_PROTOCOL: id.ws.type = ITYP_FIELD; - id.ws.field = row; index = createIndex(row, col, id.w); goto _exit; case ITYP_FIELD: - // TODO(MED): Nothing till subfield support is added - goto _exit; - - case ITYP_SUBFIELD: goto _exit; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } + Q_ASSERT(1 == 1); // Unreachable code + _exit: return index; } @@ -111,19 +110,15 @@ QModelIndex PacketModel::parent(const QModelIndex &index) const case ITYP_FIELD: parentId.ws.type = ITYP_PROTOCOL; - parentId.ws.field = 0; - parentIndex = createIndex(mPacketProtocols.indexOf(parentId.ws.protocol), 0, - parentId.w); - goto _exit; - - case ITYP_SUBFIELD: - // TODO(MED): invalid index till subfield support is added + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); goto _exit; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } + Q_ASSERT(1 == 1); // Unreachable code + _exit: return parentIndex; } @@ -140,35 +135,30 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const return QVariant(); id.w = index.internalId(); - foreach(proto, mProtocols) - { - if (proto.handle == id.ws.protocol) - goto _found; - } - return QVariant(); - -_found: switch(id.ws.type) { case ITYP_PROTOCOL: - return proto.name; + return protoName(id.ws.protocol); case ITYP_FIELD: - return proto.fieldList.at(id.ws.field).name; - - case ITYP_SUBFIELD: - return QVariant(); // TODO(MED): Till subfield support is added + return fieldName(id.ws.protocol, index.row()) + + QString(" : ") + + fieldTextValue(id.ws.protocol, index.row()).toString(); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); } /* ** --------------- Private Stuff ----------------- +** FIXME(MED): Move these to the Stream Class +** */ void PacketModel::populatePacketProtocols() { @@ -193,10 +183,13 @@ void PacketModel::populatePacketProtocols() break; case Stream::e_ft_802_3_llc: + mPacketProtocols.append(PTYP_L2_NONE); proto = PTYP_L2_802_3_LLC; break; case Stream::e_ft_snap: + mPacketProtocols.append(PTYP_L2_NONE); + mPacketProtocols.append(PTYP_L2_802_3_LLC); proto = PTYP_L2_SNAP; break; @@ -260,29 +253,494 @@ _data: mPacketProtocols.append(PTYP_DATA); } -int PacketModel::fieldCount(uint protocol) const +int PacketModel::protoCount() const +{ + return mPacketProtocols.count(); +} + +int PacketModel::fieldCount(int protocol) const { ProtocolInfo proto; + if (protocol >= mPacketProtocols.count()) + return 0; + foreach(proto, mProtocols) { - if (proto.handle == protocol) + if (proto.handle == mPacketProtocols.at(protocol)) { qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); qDebug("fieldcount = %d", proto.fieldList.size()); - return proto.fieldList.size(); + return proto.fieldList.count(); } } return 0; } -int PacketModel::subfieldCount(uint protocol, int field) const +QString PacketModel::protoName(int protocol) const { - // TODO(MED): Till subfield support is added - return 0; + ProtocolInfo proto; + + if (protocol >= mPacketProtocols.count()) + return 0; + + foreach(proto, mProtocols) + { + if (proto.handle == mPacketProtocols.at(protocol)) + { + qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); + qDebug("fieldcount = %d", proto.fieldList.size()); + return proto.name; + } + } + + return QString(); } +QString PacketModel::fieldName(int protocol, int field) const +{ + ProtocolInfo proto; + + if (protocol >= mPacketProtocols.count()) + return 0; + + foreach(proto, mProtocols) + { + if (proto.handle == mPacketProtocols.at(protocol)) + { + qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); + qDebug("fieldcount = %d", proto.fieldList.size()); + if (field >= proto.fieldList.count()) + return QString(); + + return proto.fieldList.at(field).name; + } + } + + return QString(); +} + +QVariant PacketModel::fieldTextValue(int protocol, int field) const +{ + if (protocol >= mPacketProtocols.count()) + return QVariant(); + + switch(mPacketProtocols.at(protocol)) + { + case PTYP_L2_NONE: + case PTYP_L2_ETH_2: + return ethField(field, FROL_TEXT_VALUE); + case PTYP_L2_802_3_RAW: + //return dot3Field(field, FROL_TEXT_VALUE); // FIXME(HIGH) + return ethField(field, FROL_TEXT_VALUE); + case PTYP_L2_802_3_LLC: + return llcField(field, FROL_TEXT_VALUE); + case PTYP_L2_SNAP: + return snapField(field, FROL_TEXT_VALUE); + + case PTYP_SVLAN: + return svlanField(field, FROL_TEXT_VALUE); + case PTYP_CVLAN: + // return cvlanField(field, FROL_TEXT_VALUE); // FIXME(HIGH) + return svlanField(field, FROL_TEXT_VALUE); + + case PTYP_L3_IP: + return ipField(field, FROL_TEXT_VALUE); + case PTYP_L3_ARP: + return QString(); // FIXME(HIGH) + + case PTYP_L4_TCP: + return tcpField(field, FROL_TEXT_VALUE); + case PTYP_L4_UDP: + return udpField(field, FROL_TEXT_VALUE); + case PTYP_L4_ICMP: + return QString(); // FIXME(HIGH) + case PTYP_L4_IGMP: + return QString(); // FIXME(HIGH) + + case PTYP_INVALID: + return QString(); // FIXME(HIGH) + case PTYP_DATA: + return QString(); // FIXME(HIGH) + } + + return QString(); +} + +QVariant PacketModel::ethField(int field, int role) const +{ + FieldInfo info; + + // FIXME(MED): Mac Addr formatting + + switch(field) + { + case 0: + info.name = QString("Destination Mac Address"); + info.textValue = QString("%1%2"). + arg(mpStream->l2.eth.dstMacMshw, 4, BASE_HEX, QChar('0')). + arg(mpStream->l2.eth.dstMacLsw, 8, BASE_HEX, QChar('0')); + break; + case 1: + info.name = QString("Source Mac Address"); + info.textValue = QString("%1%2"). + arg(mpStream->l2.eth.srcMacMshw, 4, BASE_HEX, QChar('0')). + arg(mpStream->l2.eth.srcMacLsw, 8, BASE_HEX, QChar('0')); + break; + case 2: + info.name = QString("Type"); + info.textValue = QString("0x%1"). + arg(mpStream->proto.etherType, 4, BASE_HEX, QChar('0')); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + +QVariant PacketModel::llcField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("DSAP"); + info.textValue = QString("0x%1"). + arg(mpStream->proto.dsap, 2, BASE_HEX, QChar('0')); + break; + case 1: + info.name = QString("SSAP"); + info.textValue = QString("0x%1"). + arg(mpStream->proto.ssap, 2, BASE_HEX, QChar('0')); + break; + case 2: + info.name = QString("Control"); + info.textValue = QString("0x%1"). + arg(mpStream->proto.ctl, 2, BASE_HEX, QChar('0')); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + +QVariant PacketModel::snapField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("OUI"); + info.textValue = QString("0x%1%2"). + arg(mpStream->proto.ouiMsb, 2, BASE_HEX, QChar('0')). + arg(mpStream->proto.ouiLshw, 4, BASE_HEX, QChar('0')); + break; + case 1: + info.name = QString("Type"); + info.textValue = QString("0x%1"). + arg(mpStream->proto.etherType, 4, BASE_HEX, QChar('0')); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + +QVariant PacketModel::svlanField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("TPID"); + info.textValue = QString("0x%1"). + arg(mpStream->l2.eth.stpid, 4, BASE_HEX, QChar('0')); + break; + case 1: + info.name = QString("PCP"); + info.textValue = QString("%1"). + arg(mpStream->l2.eth.svlanPrio); + break; + case 2: + info.name = QString("DE"); + info.textValue = QString("%1"). + arg(mpStream->l2.eth.svlanCfi); + break; + case 3: + info.name = QString("VlanId"); + info.textValue = QString("%1"). + arg(mpStream->l2.eth.svlanId); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + + +QVariant PacketModel::ipField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("Version"); + info.textValue = QString("%1"). + arg(mpStream->l3.ip.ver); + break; + case 1: + info.name = QString("Header Length"); + info.textValue = QString("%1"). + arg(mpStream->l3.ip.hdrLen); + break; + case 2: + info.name = QString("TOS/DSCP"); + info.textValue = QString("0x%1"). + arg(mpStream->l3.ip.tos, 2, BASE_HEX, QChar('0')); + break; + case 3: + info.name = QString("Total Length"); + info.textValue = QString("%1"). + arg(mpStream->l3.ip.totLen); + break; + case 4: + info.name = QString("ID"); + info.textValue = QString("0x%1"). + arg(mpStream->l3.ip.id, 2, BASE_HEX, QChar('0')); + break; + case 5: + info.name = QString("Flags"); + info.textValue = QString("0x%1"). + arg(mpStream->l3.ip.flags, 2, BASE_HEX, QChar('0')); // FIXME(HIGH) + break; + case 6: + info.name = QString("Fragment Offset"); + info.textValue = QString("%1"). + arg(mpStream->l3.ip.fragOfs); + break; + case 7: + info.name = QString("TTL"); + info.textValue = QString("%1"). + arg(mpStream->l3.ip.ttl); + break; + case 8: + info.name = QString("Protocol Type"); + info.textValue = QString("0x%1"). + arg(mpStream->l3.ip.proto, 2, BASE_HEX, QChar('0')); + break; + case 9: + info.name = QString("Checksum"); + info.textValue = QString("0x%1"). + arg(mpStream->l3.ip.cksum, 4, BASE_HEX, QChar('0')); + break; + case 10: + info.name = QString("Source IP"); + info.textValue = QHostAddress(mpStream->l3.ip.srcIp).toString(); + break; + case 11: + info.name = QString("Destination IP"); + info.textValue = QHostAddress(mpStream->l3.ip.dstIp).toString(); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + + +QVariant PacketModel::tcpField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("Source Port"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.srcPort); + break; + case 1: + info.name = QString("Destination Port"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.dstPort); + break; + case 2: + info.name = QString("Seq Number"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.seqNum); + break; + case 3: + info.name = QString("Ack Number"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.ackNum); + break; + case 4: + info.name = QString("Header Length"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.hdrLen); + break; + case 5: + info.name = QString("Reserved"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.rsvd); + break; + case 6: + info.name = QString("Flags"); + info.textValue = QString("0x%1"). + arg(mpStream->l4.tcp.flags, 2, BASE_HEX, QChar('0')); + break; + case 7: + info.name = QString("Window"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.flags); + break; + case 8: + info.name = QString("Checksum"); + info.textValue = QString("0x%1"). + arg(mpStream->l4.tcp.cksum, 4, BASE_HEX, QChar('0')); + break; + case 9: + info.name = QString("Urgent Pointer"); + info.textValue = QString("%1"). + arg(mpStream->l4.tcp.urgPtr); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + + +QVariant PacketModel::udpField(int field, int role) const +{ + FieldInfo info; + + switch(field) + { + case 0: + info.name = QString("Source Port"); + info.textValue = QString("%1"). + arg(mpStream->l4.udp.srcPort); + break; + case 1: + info.name = QString("Destination Port"); + info.textValue = QString("%1"). + arg(mpStream->l4.udp.dstPort); + break; + case 2: + info.name = QString("Total Length"); + info.textValue = QString("%1"). + arg(mpStream->l4.udp.totLen); + break; + case 3: + info.name = QString("Checksum"); + info.textValue = QString("0x%1"). + arg(mpStream->l4.udp.cksum, 4, BASE_HEX, QChar('0')); + break; + default: + info.name = QString(); + info.textValue = QString(); + } + + switch(role) + { + case FROL_NAME: + return info.name; + case FROL_TEXT_VALUE: + return info.textValue; + default: + ; + } + + Q_ASSERT(1 == 1); // Unreachable code + return QVariant(); +} + + + /* ** ------------- Registration Functions --------------- */ @@ -331,18 +789,11 @@ void PacketModel::registerFrameTypeProto() registerField(PTYP_L2_802_3_RAW, "Length", "len"); registerProto(PTYP_L2_802_3_LLC, "802.3 LLC", "dot3llc"); - registerField(PTYP_L2_802_3_LLC, "Destination Mac", "dstMac"); - registerField(PTYP_L2_802_3_LLC, "Source Mac", "srcMac"); registerField(PTYP_L2_802_3_LLC, "Destination Service Acces Point", "dsap"); registerField(PTYP_L2_802_3_LLC, "Source Service Acces Point", "ssap"); registerField(PTYP_L2_802_3_LLC, "Control", "ctl"); - registerProto(PTYP_L2_SNAP, "802.3 LLC SNAP", "dot3snap"); - registerField(PTYP_L2_SNAP, "Destination Mac", "dstMac"); - registerField(PTYP_L2_SNAP, "Source Mac", "srcMac"); - registerField(PTYP_L2_SNAP, "Destination Service Acces Point", "dsap"); - registerField(PTYP_L2_SNAP, "Source Service Access Point", "ssap"); - registerField(PTYP_L2_SNAP, "Control", "ctl"); + registerProto(PTYP_L2_SNAP, "SNAP", "dot3snap"); registerField(PTYP_L2_SNAP, "Organisationally Unique Identifier", "oui"); registerField(PTYP_L2_SNAP, "Type", "type"); diff --git a/client/packetmodel.h b/client/packetmodel.h index 6b2e82f..4f79155 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -24,20 +24,21 @@ private: quint32 w; struct { - quint8 type; + quint16 type; #define ITYP_PROTOCOL 1 #define ITYP_FIELD 2 -#define ITYP_SUBFIELD 3 - quint8 protocol; - quint8 field; - quint8 subfield; + quint16 protocol; } ws; } IndexId; + Stream *mpStream; + QList mPacketProtocols; + typedef struct { QString name; QString abbr; + QString textValue; } FieldInfo; typedef struct @@ -48,10 +49,7 @@ private: QList fieldList; } ProtocolInfo; - Stream *mpStream; - QList mPacketProtocols; QList mProtocols; - void registerProto(uint handle, char *name, char *abbr); void registerField(uint protoHandle, char *name, char *abbr); @@ -67,13 +65,26 @@ private: void registerInvalidProto(); void populatePacketProtocols(); - int fieldCount(uint protocol) const; - int subfieldCount(uint protocol, int field) const; + + int protoCount() const; + int fieldCount(int protocol) const; + QString protoName(int protocol) const; + QString fieldName(int protocol, int field) const; + QVariant fieldTextValue(int protocol, int field) const; + + QVariant ethField(int field, int role) const; + QVariant llcField(int field, int role) const; + QVariant snapField(int field, int role) const; + QVariant svlanField(int field, int role) const; + QVariant ipField(int field, int role) const; + QVariant tcpField(int field, int role) const; + QVariant udpField(int field, int role) const; // FIXME(HIGH): Is this how I want this? #define PTYP_L2_NONE 1 #define PTYP_L2_ETH_2 2 #define PTYP_L2_802_3_RAW 3 + #define PTYP_L2_802_3_LLC 4 #define PTYP_L2_SNAP 5 @@ -91,6 +102,10 @@ private: #define PTYP_INVALID 0 #define PTYP_DATA 0xFF +#define FROL_NAME 1 +#define FROL_TEXT_VALUE 2 + +#define BASE_HEX 16 }; #endif From a009fcffdff8538c3adccff8457d687533587f1f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2008 07:40:41 +0000 Subject: [PATCH 005/294] Deleted unused files --- client/mythread.cpp | 24 ----- client/mythread.h | 23 ----- client/ostinato.pro | 1 - client/portgrouplistmodel.h | 55 ----------- client/portlistmodel.cpp | 191 ------------------------------------ client/portlistmodel.h | 37 ------- client/streamlistmodel.cpp | 119 ---------------------- client/streamlistmodel.h | 46 --------- 8 files changed, 496 deletions(-) delete mode 100644 client/mythread.cpp delete mode 100644 client/mythread.h delete mode 100644 client/portgrouplistmodel.h delete mode 100644 client/portlistmodel.cpp delete mode 100644 client/portlistmodel.h delete mode 100644 client/streamlistmodel.cpp delete mode 100644 client/streamlistmodel.h diff --git a/client/mythread.cpp b/client/mythread.cpp deleted file mode 100644 index 69fe660..0000000 --- a/client/mythread.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "mythread.h" - - void MyThread::run() - { - int i, j; - - for (i=0; i - -#define NumPorts 2 -#define NumStats 8 - - class MyThread : public QThread - { - Q_OBJECT - - public: - void run(); - - signals: - void portStatsUpdate(int port, void *stats); - - private: - int stats[2][8]; - }; - -#endif diff --git a/client/ostinato.pro b/client/ostinato.pro index aceb998..07dcdc0 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -6,7 +6,6 @@ HEADERS += \ dumpview.h \ hexlineedit.h \ mainwindow.h \ - mythread.h \ packetmodel.h \ port.h \ portgroup.h \ diff --git a/client/portgrouplistmodel.h b/client/portgrouplistmodel.h deleted file mode 100644 index 4242539..0000000 --- a/client/portgrouplistmodel.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _PORT_GROUP_LIST_H -#define _PORT_GROUP_LIST_H - -#include "portgroup.h" -#include - -/* ----------------------------------------------------------- -** NOTE: FILE NOT USED 'COZ MOC DOESN'T SUPPORT NESTED CLASSES -*-------------------------------------------------------------*/ - - -class PortGroupList; - -class PortGroupList : public QObject { - - Q_OBJECT - - class PortListModel : public QAbstractItemModel - { - // This is currently a read only model - Q_OBJECT - - PortGroupList *pgl; // FIXME(HIGH): rename member - - public: - PortListModel(PortGroupList *p, QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; - - friend class PortGroupList; // FIXME(MED): Review need for friend - }; - - friend class PortListModel; - - QList mPortGroups; - PortListModel mPortGroupListModel; - -// Methods -public: - PortGroupList::PortGroupList(); - QAbstractItemModel* getModel(); - int addPortGroup(PortGroup &portGroup); - int removePortGroup(PortGroup &portGroup); - -private slots: - void when_portlist_dataChanged(); -}; - -#endif diff --git a/client/portlistmodel.cpp b/client/portlistmodel.cpp deleted file mode 100644 index df53fb9..0000000 --- a/client/portlistmodel.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "portlistmodel.h" - ------------------------ -This file is not used ------------------------ - -PortListModel::PortListModel(QObject *parent) - : QAbstractItemModel(parent) -{ - portList = new QList; - -#if 0 // FIXME: Dummy data only for testing; to be removed - PortGroup pg; - - pg.name = "A"; - pg.isLocal = TRUE; - pg.numPorts = 3; - pg.port = new Port[pg.numPorts]; - pg.port[0].portId = 0; - pg.port[0].name = "A0"; - pg.port[0].desc = "a0a0a0a0a0a0"; - pg.port[1].portId = 1; - pg.port[1].name = "A1"; - pg.port[1].desc = "a1a1a1a1a1a1"; - pg.port[2].portId = 2; - pg.port[2].name = "A2"; - pg.port[2].desc = "a2a2a2a2a2a2"; - - portList->append(pg); - - pg.name = "B"; - pg.isLocal = FALSE; - pg.numPorts = 2; - pg.port = new Port[pg.numPorts]; - pg.port[0].portId = 0; - pg.port[0].name = "B0"; - pg.port[0].desc = "b0b0b0b0b0b0"; - pg.port[1].portId = 1; - pg.port[1].name = "B1"; - pg.port[1].desc = "b1b1b1b1b1b1"; - - portList->append(pg); -#endif - // Do I need to do anything here? -} - -int PortListModel::rowCount(const QModelIndex &parent) const -{ - // qDebug("RowCount Enter\n"); - if (!parent.isValid()) - { - // Top Level Item - // qDebug("RowCount top\n"); - // qDebug("RowCount Exit: %d\n", portList->size()); - return portList->size(); - } - // qDebug("RowCount non top %d, %d, %llx\n", - // parent.row(), parent.column(), parent.internalId()); - - quint16 p = parent.internalId() & 0xFFFF; - if (p == 0xFFFF) - { - // qDebug("RowCount Exit: %d\n", portList->at(parent.row()).numPorts); - return portList->at(parent.row()).numPorts; - } - else - { - // Leaf Item - return 0; - } -} - -int PortListModel::columnCount(const QModelIndex &parent ) const -{ - return 1; // FIXME: hardcoding -} - -Qt::ItemFlags PortListModel::flags(const QModelIndex &index) const -{ - return QAbstractItemModel::flags(index); // FIXME: no need for this func -} -QVariant PortListModel::data(const QModelIndex &index, int role) const -{ - //qDebug("Enter PortListModel data\n"); - // Check for a valid index - if (!index.isValid()) - return QVariant(); - - - // Check role - if ((role == Qt::DisplayRole)) - { -#if 0 // Only for debug - qDebug("Exit PortListModel data\n"); - return "Testing"; // FIXME: for dbg only -#endif - - QModelIndex parent = index.parent(); - - if (!parent.isValid()) - { - // Top Level Item - return QString("%1 (%2) [%3]"). - arg(portList->at(index.row()).name). - arg(portList->at(index.row()).isLocal == TRUE? "LOCAL" : "REMOTE"). - arg(portList->at(index.row()).numPorts); - } - return QString("%1: %2 (%3)"). - arg(portList->at(parent.row()).port[index.row()].portId). - arg(portList->at(parent.row()).port[index.row()].name). - arg(portList->at(parent.row()).port[index.row()].desc); - } - else - return QVariant(); -} - -QVariant PortListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) - return QVariant(); - else - return QString("%1").arg(section+1); -} - -QModelIndex PortListModel::index (int row, int col, - const QModelIndex & parent) const -{ -#if 0 - if (!hasIndex(row, col, parent)) - return QModelIndex(); -#endif - - //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", - // row, col, parent.row(), parent.column(), parent.internalId()); - - if (!parent.isValid()) - { - // Top Level Item - quint16 pg = row, p = 0xFFFF; - quint32 id = (pg << 16) | p; - //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - - return createIndex(row, col, id); - } - else - { - quint16 pg = parent.row(), p = row; - quint32 id = (pg << 16) | p; - //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - - return createIndex(row, col, id); - } -} - -QModelIndex PortListModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - //qDebug("parent: R=%d, C=%d ID=%llx\n", - // index.row(), index.column(), index.internalId()); - - quint16 pg = index.internalId() >> 16; - quint16 p = index.internalId() & 0x0000FFFF; - - //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); - - if (p == 0xFFFF) - { - //qDebug("parent ret: NULL\n"); - // Top Level Item - PG - return QModelIndex(); - } - - quint32 id = (pg << 16) | 0xFFFF; - //qDebug("parent ret: R=%d, C=%d, ID=%x\n", - // pg, 0, id); - - return createIndex(pg, 0, id); - -} - -void PortListModel::doRefresh() -{ - emit layoutChanged(); -} - - diff --git a/client/portlistmodel.h b/client/portlistmodel.h deleted file mode 100644 index 45a07bc..0000000 --- a/client/portlistmodel.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _PORT_LIST_MODEL_H -#define _PORT_LIST_MODEL_H - - - ----------------- - This file is not used - ------------------ - -#include -#include -#include "portgroup.h" - -static QStringList PortListCols = (QStringList() - << "Name" -); - -class PortListModel : public QAbstractItemModel -{ - Q_OBJECT - - public: - //PortListModel(QObject *parent = 0) : QAbstractItemModel(parent) {} - PortListModel(QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; - - void doRefresh(); // FIXME: temp till model exports functions to insert rows - QList *portList; // FIXME: this shd be private -}; - -#endif diff --git a/client/streamlistmodel.cpp b/client/streamlistmodel.cpp deleted file mode 100644 index 9812720..0000000 --- a/client/streamlistmodel.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "streamlistmodel.h" - -StreamListModel::StreamListModel(QObject *parent) - : QAbstractTableModel(parent) -{ - uint i; - - // Enable all streams by default - for (i=0; i= MAX_ROWS) - return QVariant(); - - if (index.column() >= MAX_COLS) - return QVariant(); - - // Check role - if (index.column() == 0) // Icon - { - if ((role == Qt::DisplayRole)) - return QString("EDIT"); - else - return QVariant(); - } - else if (index.column() == 1) // Name - { - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return streamList[index.row()].streamName; - else - return QVariant(); - } - else if (index.column() == 2) // Enabled? - { - //if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) - // return streamList[index.row()].isEnabled ? Qt::Checked : Qt::Unchecked; - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return streamList[index.row()].isEnabled; - else - return QVariant(); - } -} - -bool StreamListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.isValid() && role == Qt::EditRole) - { - if (index.column() == 1) // Name - streamList[index.row()].streamName = value.toString(); - else if (index.column() == 2) // Enabled? - streamList[index.row()].isEnabled = value.toBool(); - else - return false; - - return true; - } - return false; -} - -QVariant StreamListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) - return StreamListCols.at(section); - else - return QString("%1").arg(section+1); -} - -#if 0 -// QModelIndex StreamListModel::index (int portNum, PortStat stat, const QModelIndex & parent = QModelIndex() ) const - -void StreamListModel::on_portStatsUpdate(int port, void*stats) -{ - int i; - QModelIndex topLeft = index(port, 0, QModelIndex()); - QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); - - for (i = 0; i < e_STAT_MAX; i++) - dummyStats[port][i] = ((int *)stats)[i]; - - emit dataChanged(topLeft, bottomRight); -} - -#endif diff --git a/client/streamlistmodel.h b/client/streamlistmodel.h deleted file mode 100644 index 54c222b..0000000 --- a/client/streamlistmodel.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _STREAM_LIST_MODEL_H -#define _STREAM_LIST_MODEL_H - -#include -#include - -static QStringList StreamListCols = (QStringList() - << "Icon" - << "Name" - << "Enable" -); - -#define MAX_ROWS 5 -#define MAX_COLS 3 - -class StreamListModel : public QAbstractTableModel -{ - Q_OBJECT - - public: - //StreamListModel(QObject *parent = 0) : QAbstractTableModel(parent) {} - StreamListModel(QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - // QModelIndex index (int portNum, PortStat stat, const QModelIndex & parent = QModelIndex() ) const; - - public slots: -// void on_portStatsUpdate(int port, void*stats); - - private: - // FIXME: temp -//#define NUM_PORTS 2 -// int dummyStats[NUM_PORTS][e_STAT_MAX]; - struct { - QString streamName; - bool isEnabled; - } streamList[MAX_ROWS]; - -}; - -#endif From f220482876104bed3c948886a16e9742b64106c0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 9 Aug 2008 03:22:13 +0000 Subject: [PATCH 006/294] Added Google Protocol Buffers as the serialization format between client and server. Initial Checkin. PB related code not yet complete --- Makefile | 5 + client/hexlineedit.cpp | 3 +- client/ostinato.pro | 8 +- client/packetmodel.cpp | 17 +- client/port.cpp | 74 ++- client/port.h | 71 ++- client/portgroup.cpp | 143 +++++- client/portgroup.h | 45 +- client/portstatsmodel.cpp | 4 + client/portswindow.cpp | 1 - client/stream.cpp | 11 + client/stream.h | 862 +++++++++++++++++++++++----------- client/streamconfigdialog.cpp | 233 +++++---- client/streamconfigdialog.h | 3 +- client/streamconfigdialog.ui | 148 +++--- client/streammodel.cpp | 2 +- common/Makefile | 14 + common/protocol.proto | 302 ++++++++++++ rpc/pbhelper.h | 66 +++ rpc/pbrpc.pro | 13 + rpc/pbrpcchannel.cpp | 189 ++++++++ rpc/pbrpcchannel.h | 74 +++ rpc/pbrpccommon.h | 52 ++ rpc/pbrpccontroller.h | 27 ++ rpc/rpcserver.cpp | 180 +++++++ rpc/rpcserver.h | 44 ++ server/abstracthost.h | 2 + server/drone.cpp | 17 +- server/drone.h | 19 +- server/drone.pro | 10 +- server/myservice.cpp | 290 ++++++++++++ server/myservice.h | 146 ++++++ server/rxtx.cpp | 10 +- server/rxtx.h | 8 + 34 files changed, 2598 insertions(+), 495 deletions(-) create mode 100644 Makefile create mode 100644 common/Makefile create mode 100644 common/protocol.proto create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c24ac11 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: + $(MAKE) -C rpc install + $(MAKE) -C common + $(MAKE) -C server + $(MAKE) -C client diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index a983bfd..85d5ccf 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -1,7 +1,8 @@ #include "hexlineedit.h" #include "qdebug.h" -QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets); +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + HexLineEdit::HexLineEdit( QWidget * parent) : QLineEdit(parent) { diff --git a/client/ostinato.pro b/client/ostinato.pro index 07dcdc0..4b1cb69 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,6 +1,8 @@ TEMPLATE = app CONFIG += qt debug QT += network +INCLUDEPATH += "c:\msys\1.0\local\include" "..\rpc\" +LIBS += -L"c:\msys\1.0\local\lib" -lprotobuf -L"..\rpc\debug" -lpbrpc RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ @@ -31,7 +33,6 @@ SOURCES += \ hexlineedit.cpp \ main.cpp \ mainwindow.cpp \ - mythread.cpp \ packetmodel.cpp \ port.cpp \ portgroup.cpp \ @@ -44,5 +45,10 @@ SOURCES += \ streamconfigdialog.cpp \ streammodel.cpp +# Protocol Buffer Sources + +SOURCES += \ + ..\common\protocol.pb.cc + # TODO(LOW): Test only include(modeltest.pri) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 9064b1d..8711b53 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -167,6 +167,7 @@ void PacketModel::populatePacketProtocols() // Clear the protocols list mPacketProtocols.clear(); +#if 0 // FIXME: protobuf // Check and populate L2 Protocol switch(mpStream->proto.ft) { @@ -251,6 +252,7 @@ void PacketModel::populatePacketProtocols() _data: mPacketProtocols.append(PTYP_DATA); +#endif } int PacketModel::protoCount() const @@ -373,7 +375,7 @@ QVariant PacketModel::ethField(int field, int role) const FieldInfo info; // FIXME(MED): Mac Addr formatting - +#if 0 // FIXME protobuf switch(field) { case 0: @@ -409,6 +411,7 @@ QVariant PacketModel::ethField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -416,6 +419,7 @@ QVariant PacketModel::llcField(int field, int role) const { FieldInfo info; +#if 0 // FIXME: protobuf switch(field) { case 0: @@ -449,6 +453,7 @@ QVariant PacketModel::llcField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -456,6 +461,7 @@ QVariant PacketModel::snapField(int field, int role) const { FieldInfo info; +#if 0 // FIXME: protobuf switch(field) { case 0: @@ -485,6 +491,7 @@ QVariant PacketModel::snapField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -492,6 +499,7 @@ QVariant PacketModel::svlanField(int field, int role) const { FieldInfo info; +#if 0 // FIXME: protobuf switch(field) { case 0: @@ -530,6 +538,7 @@ QVariant PacketModel::svlanField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -538,6 +547,7 @@ QVariant PacketModel::ipField(int field, int role) const { FieldInfo info; +#if 0 // FIXME: protobuf switch(field) { case 0: @@ -614,6 +624,7 @@ QVariant PacketModel::ipField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -622,6 +633,7 @@ QVariant PacketModel::tcpField(int field, int role) const { FieldInfo info; +#if 0 // FIXME: protobuf switch(field) { case 0: @@ -690,6 +702,7 @@ QVariant PacketModel::tcpField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } @@ -698,6 +711,7 @@ QVariant PacketModel::udpField(int field, int role) const { FieldInfo info; +#if 0 // FIXME:protobuf switch(field) { case 0: @@ -736,6 +750,7 @@ QVariant PacketModel::udpField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code +#endif return QVariant(); } diff --git a/client/port.cpp b/client/port.cpp index f7122d3..2196e7d 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -1,17 +1,82 @@ +#include + +#include + #include "port.h" +#include "pbhelper.h" + Port::Port(quint32 id, quint32 portGroupId) { - mPortId = id; + d.set_port_id(id); mPortGroupId = portGroupId; - mAdminStatus = AdminDisable; - mOperStatus = OperDown; - mControlMode = ControlShared; +#if 0 // PB // FIXME(HI): TEST only for(int i = 0; i < 10; i++) mPortStats[i] = mPortGroupId*10000+mPortId*100+i; +#endif +} + +void Port::updatePortConfig(OstProto::PortConfig *portConfig) +{ + + PbHelper pbh; + + pbh.update(&d, portConfig); +#if 0 + const ::google::protobuf::Message::Reflection *ref1; + ::google::protobuf::Message::Reflection *ref2; + std::vector list; + + qDebug("In %s", __FUNCTION__); + + ref1 = portConfig.GetReflection(); + ref1->ListFields(&list); + + ref2 = d.GetReflection(); + + for (uint i=0; i < list.size(); i++) + { + const ::google::protobuf::FieldDescriptor *f1, *f2; + + f1 = list[i]; + f2 = d.GetDescriptor()->FindFieldByName(f1->name()); + switch(f2->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + ref2->SetUInt32(f2, ref1->GetUInt32(f1)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + ref2->SetBool(f2, ref1->GetBool(f1)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + ref2->SetString(f2, ref1->GetString(f1)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + + if (msg->GetDescriptor() != OstProto::PortConfig::descriptor()) + { + qDebug("%s: invalid Message Descriptor (%s)", __FUNCTION__, + msg->GetDescriptor()->name()); + goto _error_exit; + } + + portConfig = msg; + + // check for "required" param + if (!portConfig.has_port_id()) + { + qDebug("%s: invalid Message Descriptor (%s)", __FUNCTION__, + msg->GetDescriptor()->name()); + goto _error_exit; + } +#endif } void Port::insertDummyStreams() @@ -24,3 +89,4 @@ void Port::insertDummyStreams() #endif } + diff --git a/client/port.h b/client/port.h index 13795ff..f308b3b 100644 --- a/client/port.h +++ b/client/port.h @@ -6,48 +6,77 @@ #include #include "stream.h" +class StreamModel; + class Port { - - friend class StreamModel; + +#if 0 // PB friend class PortStatsModel; +#endif + friend class StreamModel; + + //friend class PbHelper; + + // FIXME: non-friend mechanism + //friend QList* StreamModel::currentPortStreamList(void); + +private: + OstProto::PortConfig d; + + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + QList mStreams; + +#if 0 // PB + quint32 mPortId; + QString mName; + QString mDescription; + AdminStatus mAdminStatus; + OperStatus mOperStatus; + ControlMode mControlMode; + + quint32 mPortStats[10]; // FIXME(HI):Hardcoding +#endif public: enum AdminStatus { AdminDisable, AdminEnable }; enum OperStatus { OperDown, OperUp }; enum ControlMode { ControlShared, ControlExclusive }; -private: - quint32 mPortId; - quint32 mPortGroupId; - QList mStreams; - QString mName; - QString mDescription; - QString mUserAlias; // user defined - AdminStatus mAdminStatus; - OperStatus mOperStatus; - ControlMode mControlMode; - - quint32 mPortStats[10]; // FIXME(HI):Hardcoding - -public: // FIXME(HIGH): default args is a hack for QList operations on Port Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); - quint32 id() const { return mPortId; } quint32 portGroupId() const { return mPortGroupId; } - const QString& name() const { return mName; } - const QString& description() const { return mDescription; } const QString& userAlias() const { return mUserAlias; } - void setName(QString &name) { mName = name; } + quint32 id() const + { return d.port_id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + OperStatus operStatus() + { return (d.is_oper_up()?OperUp:OperDown); } + ControlMode controlMode() + { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } + +#if 0 + void setName(QString &name) { d.name; } void setName(const char* name) { mName = QString(name); } void setDescription(QString &description) { mDescription = description; } void setDescription(const char *description) { mDescription = QString(description); } - +#endif //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString &alias) { mUserAlias = alias; } //void setExclusive(bool flag); + + void updatePortConfig(OstProto::PortConfig *portConfig); + // FIXME(HIGH): Only for testing void insertDummyStreams(); }; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index e8a7dfe..fa5fa0e 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,6 +1,8 @@ #include "portgroup.h" #include "../common/protocol.h" +#include + quint32 PortGroup::mPortGroupAllocId = 0; PortGroup::PortGroup(QHostAddress ip, quint16 port) @@ -8,48 +10,67 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) // Allocate an id for self mPortGroupId = PortGroup::mPortGroupAllocId++; +#if 0 // PB // Init attributes for which we values were passed to us mServerAddress = ip; mServerPort = port; // Init remaining attributes with defaults mpSocket = new QTcpSocket(this); +#endif + rpcChannel = new PbRpcChannel(ip, port); + rpcController = new PbRpcController(); + serviceStub = new OstProto::OstService::Stub(rpcChannel, + OstProto::OstService::STUB_OWNS_CHANNEL); + +#if 0 // PB // TODO: consider using QT's signal-slot autoconnect connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_mpSocket_stateChanged())); connect(mpSocket, SIGNAL(connected()), this, SLOT(when_connected())); connect(mpSocket, SIGNAL(disconnected()), this, SLOT(when_disconnected())); connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(when_error(QAbstractSocket::SocketError))); connect(mpSocket, SIGNAL(readyRead()), this, SLOT(when_dataAvail())); +#endif + // FIXME:Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged())); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); } PortGroup::~PortGroup() { qDebug("PortGroup Destructor"); - // Disconnect and free TCP mpSocketet etc. + // Disconnect and free rpc channel etc. PortGroup::disconnectFromHost(); - delete mpSocket; + delete serviceStub; } +#if 0 // PB void PortGroup::connectToHost(QHostAddress ip, quint16 port) { - mServerAddress = ip; - mServerPort = port; - - PortGroup::connectToHost(); + rpcChannel->establish(ip, port) } void PortGroup::connectToHost() { qDebug("PortGroup::connectToHost()"); - mpSocket->connectToHost(mServerAddress, mServerPort); + rpcChannel->establish() } void PortGroup::disconnectFromHost() { mpSocket->disconnectFromHost(); } +#endif +#if 0 // PB // -------------------------------------------- // Private Methods // -------------------------------------------- @@ -116,22 +137,32 @@ _next: return; } +#endif // ------------------------------------------------ // Slots // ------------------------------------------------ -void PortGroup::on_mpSocket_stateChanged() +void PortGroup::on_rpcChannel_stateChanged() { qDebug("state changed"); emit portGroupDataChanged(this); } -void PortGroup::when_connected() +void PortGroup::on_rpcChannel_connected() { - qDebug("connected\n"); + OstProto::Void void_; + OstProto::PortIdList *portIdList; + qDebug("connected\n"); emit portGroupDataChanged(this); + qDebug("requesting portlist ..."); + portIdList = new OstProto::PortIdList(); + rpcController->Reset(); + serviceStub->getPortIdList(rpcController, &void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, portIdList)); + +#if 0 // PB // Ask for Port Capability tCommHdr pkt; pkt.ver = 1; @@ -141,9 +172,10 @@ void PortGroup::when_connected() pkt.msgLen = HTONS(8); mpSocket->write((char*) &pkt, sizeof(pkt)); +#endif } -void PortGroup::when_disconnected() +void PortGroup::on_rpcChannel_disconnected() { qDebug("disconnected\n"); emit portListAboutToBeChanged(mPortGroupId); @@ -152,12 +184,13 @@ void PortGroup::when_disconnected() emit portGroupDataChanged(this); } -void PortGroup::when_error(QAbstractSocket::SocketError socketError) +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { qDebug("error\n"); emit portGroupDataChanged(this); } +#if 0 // PB void PortGroup::when_dataAvail() { qDebug("dataAvail\n"); @@ -165,5 +198,91 @@ void PortGroup::when_dataAvail() QByteArray msg = mpSocket->read(1024); // FIXME: hardcoding ProcessMsg(msg.constData(), msg.size()); } +#endif +void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) +{ + int count; + qDebug("got a portlist ..."); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + count = portIdList->port_id_size(); + qDebug("%s: portid count = %d", __FUNCTION__, count); + qDebug("%s: %s", __FUNCTION__, portIdList->DebugString().c_str()); + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < count; i++) + { + Port *p; + + p = new Port(portIdList->port_id(i), mPortGroupId); + //p->setName("name"); + //p->setDescription("Desc"); + p->insertDummyStreams(); // FIXME: only for testing + qDebug("before port append\n"); + mPorts.append(*p); + } + + emit portListChanged(mPortGroupId); + + // Request PortConfigList + { + OstProto::PortConfigList *portConfigList; + + qDebug("requesting port config list ..."); + portConfigList = new OstProto::PortConfigList(); + rpcController->Reset(); + serviceStub->getPortConfig(rpcController, + portIdList, portConfigList, NewCallback(this, + &PortGroup::processPortConfigList, portConfigList)); + } + + goto _exit; + +_error_exit: +_exit: + delete portIdList; +} + +void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) +{ + int count; + + qDebug("In %s", __FUNCTION__); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + count = portConfigList->list_size(); + qDebug("%s: count = %d", __FUNCTION__, count); + qDebug("%s: <%s>", __FUNCTION__, portConfigList->DebugString().c_str()); + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < count; i++) + { + uint id; + + id = portConfigList->list(i).port_id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id].updatePortConfig(portConfigList->mutable_list(i)); + } + + emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + +_error_exit: + delete portConfigList; +} diff --git a/client/portgroup.h b/client/portgroup.h index 8f5c1e3..528f48b 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -5,6 +5,9 @@ #include #include +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + /* TODO HIGH MED @@ -21,10 +24,14 @@ private: quint32 mPortGroupId; static quint32 mPortGroupAllocId; QString mUserAlias; // user defined - +#if 0 // PB QTcpSocket *mpSocket; QHostAddress mServerAddress; quint16 mServerPort; +#endif + PbRpcChannel *rpcChannel; + ::google::protobuf::RpcController *rpcController; + OstProto::OstService::Stub *serviceStub; public: // FIXME(HIGH): member access QList mPorts; @@ -33,34 +40,46 @@ public: quint16 port = DEFAULT_SERVER_PORT); ~PortGroup(); - void connectToHost(); - void connectToHost(QHostAddress ip, quint16 port); - void disconnectFromHost(); + void connectToHost() { rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { rpcChannel->establish(ip, port); } + void disconnectFromHost() { rpcChannel->tearDown(); } int numPorts() const { return mPorts.size(); } quint32 id() const { return mPortGroupId; } - const QHostAddress& serverAddress() const { return mServerAddress; } - quint16 serverPort() const { return mServerPort; } - const QString& userAlias() const { return mUserAlias; } - QAbstractSocket::SocketState state() const { return mpSocket->state(); } + const QString& userAlias() const { return mUserAlias; } void setUserAlias(QString alias) { mUserAlias = alias; }; + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(OstProto::PortIdList *portIdList); + void processPortConfigList(OstProto::PortConfigList *portConfigList); + signals: void portGroupDataChanged(PortGroup* portGroup); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); private slots: - void on_mpSocket_stateChanged(); - void when_connected(); - void when_disconnected(); - void when_error(QAbstractSocket::SocketError socketError); - void when_dataAvail(); + void on_rpcChannel_stateChanged(); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); +#if 0 // PB + void on_rpcChannel_when_dataAvail(); +#endif private: +#if 0 // PB void ProcessCapabilityInfo(const char *msg, qint32 size); void ProcessMsg(const char *msg, quint32 size); +#endif }; #endif diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index c67c7e4..435344c 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -70,7 +70,11 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const // Check role if (role == Qt::DisplayRole) + { +#if 0 // PB return pgl->mPortGroups.at(pgidx)->mPorts.at(pidx).mPortStats[index.row()]; +#endif return 0; //FIXME: Get actual port stats + } else return QVariant(); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index c316b0d..2e6df17 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,5 +1,4 @@ #include "portswindow.h" -#include "streamlistmodel.h" #include "streamconfigdialog.h" #include #include diff --git a/client/stream.cpp b/client/stream.cpp index 980d26c..e153b8e 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -1,15 +1,25 @@ #include +quint32 Stream::mAllocId = 0; + Stream::Stream() { + mId = mAllocId++; + + mCore = new OstProto::StreamCore; + mMac = new MacProtocol; + mIp = new IpProtocol; +#if 0 // Default constructor InitDefaultMeta(); InitDefaultProto(); InitDefaultL2(); InitDefaultL3(); InitDefaultL4(); +#endif } +#if 0 void Stream::InitDefaultMeta() { // TODO(LOW): Use #defines @@ -121,3 +131,4 @@ void Stream::InitDefaultL4Udp() l4.udp.totLen = STREAM_DEF_L4_UDP_TOT_LEN; l4.udp.cksum = STREAM_DEF_L4_UDP_CKSUM; } +#endif diff --git a/client/stream.h b/client/stream.h index 4469065..4b134cb 100644 --- a/client/stream.h +++ b/client/stream.h @@ -3,17 +3,547 @@ #include #include +#include "../common/protocol.pb.h" class StreamConfigDialog; class StreamModel; class PacketModel; +// Convenience Defines FIXME +#define IP_PROTO_ICMP 0x01 +#define IP_PROTO_IGMP 0x02 +#define IP_PROTO_TCP 0x06 +#define IP_PROTO_UDP 0x11 + +#if 0 + // Protocols + struct { + FrameType ft; + + + + quint16 protoMask; +#define PM_L3_PROTO_NONE 0x0001 +#define PM_L3_PROTO_OTHER 0x0002 +#define PM_L4_PROTO_NONE 0x0004 +#define PM_L4_PROTO_OTHER 0x0008 + + quint16 etherType; +#define ETH_TYP_IP 0x0800 +#define ETH_TYP_ARP 0x0806 + + quint16 ipProto; +#define IP_PROTO_ICMP 0x01 +#define IP_PROTO_IGMP 0x02 +#define IP_PROTO_TCP 0x06 +#define IP_PROTO_UDP 0x11 + } proto; + + // L2 + struct { + // Ethernet + + + + + } eth; + } l2; +#endif + +class AbstractProtocol +{ + // TODO +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac d; + +public: + enum MacAddrMode { + MacAddrFixed, + MacAddrInc, + MacAddrDec + }; + + // Dst Mac + quint64 dstMac() + { return d.dst_mac(); } + + bool setDstMac(quint64 dstMac) + { d.set_dst_mac(dstMac); return true; } + + MacAddrMode dstMacMode() + { return (MacAddrMode) d.dst_mac_mode(); } + bool setDstMacMode(MacAddrMode dstMacMode) + { d.set_dst_mac_mode((OstProto::Mac::MacAddrMode)dstMacMode); return true; } + + quint16 dstMacCount() + { return d.dst_mac_count(); } + bool setDstMacCount(quint16 dstMacCount) + { d.set_dst_mac_count(dstMacCount); return true; } + + quint16 dstMacStep() + { return d.dst_mac_step(); } + bool setDstMacStep(quint16 dstMacStep) + { d.set_dst_mac_step(dstMacStep); return true; } + + // Src Mac + quint64 srcMac() + { return d.src_mac(); } + + bool setSrcMac(quint64 srcMac) + { d.set_src_mac(srcMac); return true; } + + MacAddrMode srcMacMode() + { return (MacAddrMode) d.src_mac_mode(); } + bool setSrcMacMode(MacAddrMode srcMacMode) + { d.set_src_mac_mode((OstProto::Mac::MacAddrMode)srcMacMode); return true; } + + quint16 srcMacCount() + { return d.src_mac_count(); } + bool setSrcMacCount(quint16 srcMacCount) + { d.set_src_mac_count(srcMacCount); return true; } + + quint16 srcMacStep() + { return d.src_mac_step(); } + bool setSrcMacStep(quint16 srcMacStep) + { d.set_src_mac_step(srcMacStep); return true; } +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc d; + +public: + quint8 dsap() + { return d.dsap(); } + bool setDsap(quint8 dsap) + { d.set_dsap(dsap); return true; } + + quint8 ssap() + { return d.ssap(); } + bool setSsap(quint8 ssap) + { d.set_ssap(ssap); return true; } + + quint8 ctl() + { return d.ctl(); } + bool setCtl(quint8 ctl) + { d.set_ctl(ctl); return true; } + +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap d; + +public: + quint32 oui() + { return d.oui(); } + bool setOui(quint32 oui) + { d.set_oui(oui); return true; } + + quint16 type() + { return d.type(); } + bool setType(quint16 type) + { d.set_type(type); return true; } +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 d; + +public: + quint16 type() + { return d.type(); } + bool setType(quint16 type) + { d.set_type(type); return true; } +}; + +class VlanProtocol : public AbstractProtocol +{ +// TODO +#if 0 + quint16 vlanMask; +#define VM_UNTAGGED 0x0000 +#define VM_CVLAN_TAGGED 0x0001 +#define VM_CVLAN_TPID_OVERRIDE 0x0002 +#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; + quint16 cvlanId : 13; + quint16 stpid; + quint16 svlanPrio : 3; + quint16 svlanCfi : 1; + quint16 svlanId : 13; +#endif +}; + +// IP +class IpProtocol : public AbstractProtocol +{ +private: + OstProto::Ip d; + +public: + + enum IpAddrMode { + IpAddrFixed, + IpAddrIncHost, + IpAddrDecHost, + IpAddrRandomHost + }; + + enum IpFlag { + IpOverrideVersion = 0x01, + IpOverrideHdrLen = 0x02, + IpOverrideTotLen = 0x03, + IpOverrideCksum = 0x04 + }; + Q_DECLARE_FLAGS(IpFlags, IpFlag); + + IpFlags ipFlags() + { + IpFlags f; + + if (d.is_override_ver()) f|= IpOverrideVersion; + if (d.is_override_hdrlen()) f|= IpOverrideHdrLen; + if (d.is_override_totlen()) f|= IpOverrideTotLen; + if (d.is_override_cksum()) f|= IpOverrideCksum; + + return f; + } + + bool setIpFlags(IpFlags ipFlags) + { + if (ipFlags.testFlag(IpOverrideVersion)) + d.set_is_override_ver(true); + else + d.set_is_override_ver(false); + + if (ipFlags.testFlag(IpOverrideHdrLen)) + d.set_is_override_hdrlen(true); + else + d.set_is_override_hdrlen(false); + + if (ipFlags.testFlag(IpOverrideTotLen)) + d.set_is_override_totlen(true); + else + d.set_is_override_totlen(false); + + if (ipFlags.testFlag(IpOverrideCksum)) + d.set_is_override_cksum(true); + else + d.set_is_override_cksum(false); + + return true; + } + + quint8 ver() + { return (d.ver_hdrlen() >> 4); } + bool setVer(quint8 ver) + { d.set_ver_hdrlen((d.ver_hdrlen() & 0x0F) | (ver << 4)); return true; } + + quint8 hdrLen() + { return (d.ver_hdrlen() & 0xF); } + bool setHdrLen(quint8 hdrLen) + { d.set_ver_hdrlen((d.ver_hdrlen() & 0xF0) | hdrLen); return true; } + + quint8 tos() + { return d.tos(); } + bool setTos(quint8 tos) + { d.set_tos(tos); return true; } + + quint16 totLen() + { return d.tot_len(); } + bool setTotLen(quint16 totLen) + { d.set_tot_len(totLen); return true; } + + quint16 id() + { return d.id(); } + bool setId(quint16 id) + { d.set_id(id); return true; } + + quint16 flags() + { return d.flags(); } + bool setFlags(quint16 flags) + { d.set_flags(flags); return true; } +#define IP_FLAG_UNUSED 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_MF 0x4 + + quint16 fragOfs() + { return d.frag_ofs(); } + bool setFragOfs(quint16 fragOfs) + { d.set_frag_ofs(fragOfs); return true; } + + quint8 ttl() + { return d.ttl(); } + bool setTtl(quint8 ttl) + { d.set_ttl(ttl); return true; } + + quint8 proto() + { return d.proto(); } + bool setProto(quint8 proto) + { d.set_proto(proto); return true; } + + quint16 cksum() + { return d.cksum(); } + bool setCksum(quint16 cksum) + { d.set_cksum(cksum); return true; } + + // Source IP + quint32 srcIp() + { return d.src_ip(); } + bool setSrcIp(quint32 srcIp) + { d.set_src_ip(srcIp); return true; } + + IpAddrMode srcIpMode() + { return (IpAddrMode) d.src_ip_mode(); } + bool setSrcIpMode(IpAddrMode srcIpMode) + { d.set_src_ip_mode((OstProto::Ip::IpAddrMode)srcIpMode); return true; } + + quint16 srcIpCount() + { return d.src_ip_count(); } + bool setSrcIpCount(quint16 srcIpCount) + { d.set_src_ip_count(srcIpCount); return true; } + + quint32 srcIpMask() + { return d.src_ip_mask(); } + bool setSrcIpMask(quint32 srcIpMask) + { d.set_src_ip_mask(srcIpMask); return true; } + + // Destination IP + quint32 dstIp() + { return d.dst_ip(); } + bool setDstIp(quint32 dstIp) + { d.set_dst_ip(dstIp); return true; } + + IpAddrMode dstIpMode() + { return (IpAddrMode) d.dst_ip_mode(); } + bool setDstIpMode(IpAddrMode dstIpMode) + { d.set_dst_ip_mode((OstProto::Ip::IpAddrMode)dstIpMode); return true; } + + quint16 dstIpCount() + { return d.dst_ip_count(); } + bool setDstIpCount(quint16 dstIpCount) + { d.set_dst_ip_count(dstIpCount); return true; } + + quint32 dstIpMask() + { return d.dst_ip_mask(); } + bool setDstIpMask(quint32 dstIpMask) + { d.set_dst_ip_mask(dstIpMask); return true; } + + // TODO: Options +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(IpProtocol::IpFlags) + +class ArpProtocol: public AbstractProtocol +{ + // TODO: ARP +}; + +// TCP +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp d; + +public: + enum TcpFlag + { + TcpOverrideHdrLen = 0x01, + TcpOverrideCksum = 0x02 + }; + Q_DECLARE_FLAGS(TcpFlags, TcpFlag); + + TcpFlags tcpFlags() + { + TcpFlags f; + + if (d.is_override_hdrlen()) f|= TcpOverrideHdrLen; + if (d.is_override_cksum()) f|= TcpOverrideCksum; + + return f; + } + + bool setTcpFlags(TcpFlags tcpFlags) + { + if (tcpFlags.testFlag(TcpOverrideHdrLen)) + d.set_is_override_hdrlen(true); + else + d.set_is_override_hdrlen(false); + + if (tcpFlags.testFlag(TcpOverrideCksum)) + d.set_is_override_cksum(true); + else + d.set_is_override_cksum(false); + + return true; + } + + quint16 srcPort() + { return d.src_port(); } + bool setSrcPort(quint16 srcPort) + { d.set_src_port(srcPort); return true; } + + quint16 dstPort() + { return d.dst_port(); } + bool setdstPort(quint16 dstPort) + { d.set_dst_port(dstPort); return true; } + + quint32 seqNum() + { return d.seq_num(); } + bool setSeqNum(quint32 seqNum) + { d.set_seq_num(seqNum); return true;} + + quint32 ackNum() + { return d.ack_num(); } + bool setAckNum(quint32 ackNum) + { d.set_ack_num(ackNum); return true;} + + quint8 hdrLen() + { return (d.hdrlen_rsvd() >> 4); } + bool setHdrLen(quint8 hdrLen) + { d.set_hdrlen_rsvd((d.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); return true; } + + quint8 rsvd() + { return (d.hdrlen_rsvd() & 0xF); } + bool setRsvd(quint8 rsvd) + { d.set_hdrlen_rsvd((d.hdrlen_rsvd() & 0xF0) | rsvd); return true; } + + + // TODO: convert to enum maybe? + quint8 flags() + { return d.flags(); } + bool setFlags(quint8 flags) + { d.set_flags(flags); return true; } +#define TCP_FLAG_URG 0x01 +#define TCP_FLAG_ACK 0x02 +#define TCP_FLAG_PSH 0x04 +#define TCP_FLAG_RST 0x08 +#define TCP_FLAG_SYN 0x10 +#define TCP_FLAG_FIN 0x20 + + quint16 window() + { return d.window(); } + bool setWindow(quint16 window) + { d.set_window(window); return true; } + + quint16 cksum() + { return d.cksum(); } + bool setCksum(quint16 cksum) + { d.set_cksum(cksum); return true; } + + quint16 urg_ptr() + { return d.urg_ptr(); } + bool seturg_ptr(quint16 urg_ptr) + { d.set_urg_ptr(urg_ptr); return true; } + +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(TcpProtocol::TcpFlags) + + +// UDP +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp d; + +public: + enum UdpFlag + { + UdpOverrideTotLen = 0x01, + UdpOverrideCksum = 0x02 + }; + Q_DECLARE_FLAGS(UdpFlags, UdpFlag); + + UdpFlags udpFlags() + { + UdpFlags f; + + if (d.is_override_totlen()) f|= UdpOverrideTotLen; + if (d.is_override_cksum()) f|= UdpOverrideCksum; + + return f; + } + + bool setUdpFlags(UdpFlags udpFlags) + { + if (udpFlags.testFlag(UdpOverrideTotLen)) + d.set_is_override_totlen(true); + else + d.set_is_override_totlen(false); + + if (udpFlags.testFlag(UdpOverrideCksum)) + d.set_is_override_cksum(true); + else + d.set_is_override_cksum(false); + + return true; + } + + quint16 srcPort() + { return d.src_port(); } + bool setSrcPort(quint16 srcPort) + { d.set_src_port(srcPort); return true; } + + quint16 dstPort() + { return d.dst_port(); } + bool setdstPort(quint16 dstPort) + { d.set_dst_port(dstPort); return true; } + + quint16 totLen() + { return d.totlen(); } + bool setTotLen(quint16 totLen) + { d.set_totlen(totLen); return true; } + + quint16 cksum() + { return d.cksum(); } + bool setCksum(quint16 cksum) + { d.set_cksum(cksum); return true; } + +}; + +class IcmpProtocol { +// TODO: ICMP +}; + +class IgmpProtocol { +// TODO: IGMP +}; + + class Stream { + static quint32 mAllocId; + + quint32 mId; + OstProto::StreamCore *mCore; + + MacProtocol *mMac; + IpProtocol *mIp; + +#if 0 friend class StreamConfigDialog; friend class StreamModel; friend class PacketModel; +#endif +public: enum FrameType { e_ft_none, e_ft_eth_2, @@ -36,273 +566,78 @@ class Stream { e_fl_random }; - enum MacAddrMode { - e_mm_fixed, - e_mm_inc, - e_mm_dec, - }; - - enum IpAddrMode { - e_im_fixed, - e_im_inc_host, - e_im_dec_host, - e_im_random_host - }; - - // Meta Data - struct { - // Data Pattern - DataPatternMode patternMode; - quint32 pattern; - quint16 dataStartOfs; - - // Frame Length (includes CRC) - FrameLengthMode lenMode; - quint16 frameLen; - quint16 frameLenMin; - quint16 frameLenMax; - } meta; - - // Protocols - struct { - FrameType ft; - - quint8 dsap; - quint8 ssap; - quint8 ctl; - quint8 ouiMsb; - quint16 ouiLshw; - - quint16 protoMask; -#define PM_L3_PROTO_NONE 0x0001 -#define PM_L3_PROTO_OTHER 0x0002 -#define PM_L4_PROTO_NONE 0x0004 -#define PM_L4_PROTO_OTHER 0x0008 - - quint16 etherType; -#define ETH_TYP_IP 0x0800 -#define ETH_TYP_ARP 0x0806 - - quint16 ipProto; -#define IP_PROTO_ICMP 0x01 -#define IP_PROTO_IGMP 0x02 -#define IP_PROTO_TCP 0x06 -#define IP_PROTO_UDP 0x11 - } proto; - - // L2 - struct { - // Ethernet - struct { - // Dst Mac - quint16 dstMacMshw; - quint32 dstMacLsw; - MacAddrMode dstMacMode; - quint16 dstMacCount; - quint16 dstMacStep; - - // srcMac - quint16 srcMacMshw; - quint32 srcMacLsw; - MacAddrMode srcMacMode; - quint16 srcMacCount; - quint16 srcMacStep; - - - quint16 vlanMask; -#define VM_UNTAGGED 0x0000 -#define VM_CVLAN_TAGGED 0x0001 -#define VM_CVLAN_TPID_OVERRIDE 0x0002 -#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; - quint16 cvlanId : 13; - quint16 stpid; - quint16 svlanPrio : 3; - quint16 svlanCfi : 1; - quint16 svlanId : 13; - } eth; - } l2; - - struct { - // IP - struct { - quint8 ipMask; -#define IM_OVERRIDE_VERSION 0x01 -#define IM_OVERRIDE_HDRLEN 0x02 -#define IM_OVERRIDE_TOTLEN 0x04 -#define IM_OVERRIDE_CKSUM 0x08 -#define STREAM_DEF_IP_MASK 0x00 - - quint8 ver : 4; -#define STREAM_DEF_L3_IP_VER 0x4 - - quint8 hdrLen : 4; -#define STREAM_DEF_L3_IP_HDR_LEN 0x5 - - quint8 tos; -#define STREAM_DEF_L3_IP_TOS 0x00 - - quint16 totLen; -#define STREAM_DEF_L3_IP_TOT_LEN 0x00 - - quint16 id; -#define STREAM_DEF_L3_IP_ID 0x1234 - - quint16 flags : 3; -#define IP_FLAG_UNUSED 0x1 -#define IP_FLAG_DF 0x2 -#define IP_FLAG_MF 0x4 -#define STREAM_DEF_L3_IP_FLAGS 0x00 - - quint16 fragOfs : 13; -#define STREAM_DEF_L3_IP_FRAG_OFS 0x0000 - - quint8 ttl; -#define STREAM_DEF_L3_IP_TTL 0x7F - - quint8 proto; -#define STREAM_DEF_L3_IP_PROTO 0x00 - - quint16 cksum; -#define STREAM_DEF_L3_IP_CKSUM 0x0000 - - // Source IP - quint32 srcIp; -#define STREAM_DEF_L3_IP_SRC_IP 0x02020202 - - IpAddrMode srcIpMode; -#define STREAM_DEF_L3_IP_SRC_IP_MODE e_im_fixed - - quint16 srcIpCount; -#define STREAM_DEF_L3_IP_SRC_IP_COUNT 16 - - quint32 srcIpMask; -#define STREAM_DEF_L3_IP_SRC_IP_MASK 0xFFFFFFFF - - // Destination IP - quint32 dstIp; -#define STREAM_DEF_L3_IP_DST_IP 0x01010101 - - IpAddrMode dstIpMode; -#define STREAM_DEF_L3_IP_DST_IP_MODE e_im_fixed - - quint16 dstIpCount; -#define STREAM_DEF_L3_IP_DST_IP_COUNT 16 - - quint32 dstIpMask; -#define STREAM_DEF_L3_IP_DST_IP_MASK 0xFFFFFFFF - - // TODO: Options - } ip; - - // TODO: ARP - struct { - } arp; - } l3; - - // L4 - struct { - // TCP - struct { - quint32 tcpMask; -#define TM_OVERRIDE_HDRLEN 0x1 -#define TM_OVERRIDE_CKSUM 0x2 -#define STREAM_DEF_L4_TCP_TCP_MASK 0x00; - - quint16 srcPort; -#define STREAM_DEF_L4_TCP_SRC_PORT 8902; - - quint16 dstPort; -#define STREAM_DEF_L4_TCP_DST_PORT 80 - - quint32 seqNum; -#define STREAM_DEF_L4_TCP_SEQ_NUM 129018 - - quint32 ackNum; -#define STREAM_DEF_L4_TCP_ACK_NUM 98223 - - quint8 hdrLen : 4; -#define STREAM_DEF_L4_TCP_HDR_LEN 0x5 - - quint8 rsvd : 4; -#define STREAM_DEF_L4_TCP_RSVD 0x0 - - quint8 flags; -#define TCP_FLAG_URG 0x01 -#define TCP_FLAG_ACK 0x02 -#define TCP_FLAG_PSH 0x04 -#define TCP_FLAG_RST 0x08 -#define TCP_FLAG_SYN 0x10 -#define TCP_FLAG_FIN 0x20 -#define STREAM_DEF_L4_TCP_FLAGS 0x00 - - - quint16 window; -#define STREAM_DEF_L4_TCP_WINDOW 1024 - - quint16 cksum; -#define STREAM_DEF_L4_TCP_CKSUM 0x0000 - - quint16 urgPtr; -#define STREAM_DEF_L4_TCP_URG_PTR 0x0000 - } tcp; - - // UDP - struct { - quint32 udpMask; -#define UM_OVERRIDE_TOTLEN 0x01 -#define UM_OVERRIDE_CKSUM 0x02 -#define STREAM_DEF_L4_UDP_UDP_MASK 0x00 - - quint16 srcPort; -#define STREAM_DEF_L4_UDP_SRC_PORT 8902 - - quint16 dstPort; -#define STREAM_DEF_L4_UDP_DST_PORT 80 - - quint16 totLen; -#define STREAM_DEF_L4_UDP_TOT_LEN 0x0000 - - quint16 cksum; -#define STREAM_DEF_L4_UDP_CKSUM 0x0000 - } udp; - - // TODO: ICMP - struct { - } icmp; - - // TODO: IGMP - struct { - } igmp; - } l4; - - QString mName; - bool mIsEnabled; - -// ------------------------------------------------------- -// Methods -// ------------------------------------------------------- -public: + // ------------------------------------------------------- + // Methods + // ------------------------------------------------------- Stream(); - int enable(bool flag); - const QString& name() const { return mName; } - bool isEnabled() const { return mIsEnabled; } - void setName(QString name) { mName = name; } - void setEnabled(bool isEnabled) { mIsEnabled = isEnabled; } + quint32 id() + { return mId;} + + quint32 ordinal() + { return mCore->ordinal();} + bool setOrderdinal(quint32 ordinal) + { mCore->set_ordinal(ordinal); return true; } + + bool isEnabled() const + { return mCore->is_enabled(); } + bool setIsEnabled(bool flag) + { mCore->set_is_enabled(flag); return true; } + + const QString name() const + { return QString().fromStdString(mCore->name()); } + bool setName(QString name) + { mCore->set_name(name.toStdString()); return true; } + + FrameType frameType() + { return (FrameType) mCore->ft(); } + bool setFrameType(FrameType frameType) + { mCore->set_ft((OstProto::StreamCore::FrameType) frameType); return true; } + + // Data Pattern + DataPatternMode patternMode() + { return (DataPatternMode) mCore->pattern_mode(); } + bool setPatternMode(DataPatternMode patternMode) + { mCore->set_pattern_mode( + (OstProto::StreamCore::DataPatternMode) patternMode); return true; } + + quint32 pattern() + { return mCore->pattern(); } + bool setPattern(quint32 pattern) + { mCore->set_pattern(pattern); return true; } + + // Frame Length (includes CRC) + FrameLengthMode lenMode() + { return (FrameLengthMode) mCore->len_mode(); } + bool setLenMode(FrameLengthMode lenMode) + { mCore->set_len_mode( + (OstProto::StreamCore::FrameLengthMode) lenMode); return true; } + + quint16 frameLen() + { return mCore->frame_len(); } + bool setFrameLen(quint16 frameLen) + { mCore->set_frame_len(frameLen); return true; } + + quint16 frameLenMin() + { return mCore->frame_len_min(); } + bool setFrameLenMin(quint16 frameLenMin) + { mCore->set_frame_len_min(frameLenMin); return true; } + + quint16 frameLenMax() + { return mCore->frame_len_max(); } + bool setFrameLenMax(quint16 frameLenMax) + { mCore->set_frame_len_max(frameLenMax); return true; } + +// TODO +#if 0 + quint16 dataStartOfs; +#endif + + MacProtocol* mac() { return mMac; } + IpProtocol* ip() { return mIp; } private: +#if 0 void InitDefaultMeta(); void InitDefaultProto(); void InitDefaultL2(); @@ -311,6 +646,7 @@ private: void InitDefaultL4(); void InitDefaultL4Tcp(); void InitDefaultL4Udp(); +#endif }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index f51c49e..8e6a8a6 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -82,9 +82,29 @@ StreamConfigDialog::~StreamConfigDialog() void StreamConfigDialog::on_cmbDstMacMode_currentIndexChanged(QString mode) { if (mode == "Fixed") + { leDstMacCount->setEnabled(FALSE); + leDstMacStep->setEnabled(FALSE); + } else + { leDstMacCount->setEnabled(TRUE); + leDstMacStep->setEnabled(TRUE); + } +} + +void StreamConfigDialog::on_cmbSrcMacMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + leSrcMacCount->setEnabled(FALSE); + leSrcMacStep->setEnabled(FALSE); + } + else + { + leSrcMacCount->setEnabled(TRUE); + leSrcMacStep->setEnabled(TRUE); + } } void StreamConfigDialog::on_pbPrev_clicked() @@ -246,7 +266,7 @@ void StreamConfigDialog::update_NumPacketsAndNumBursts() leNumBursts->setEnabled(false); } -QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets) +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets) { int i; QChar zero('0'); @@ -291,19 +311,19 @@ void StreamConfigDialog::LoadCurrentStream() // Meta Data { - cmbPatternMode->setCurrentIndex(pStream->meta.patternMode); - lePattern->setText(uintToHexStr(pStream->meta.pattern, str, 4)); + cmbPatternMode->setCurrentIndex(pStream->patternMode()); + lePattern->setText(uintToHexStr(pStream->pattern(), str, 4)); - cmbPktLenMode->setCurrentIndex(pStream->meta.lenMode); - lePktLen->setText(str.setNum(pStream->meta.frameLen)); - lePktLenMin->setText(str.setNum(pStream->meta.frameLenMin)); - lePktLenMax->setText(str.setNum(pStream->meta.frameLenMax)); + cmbPktLenMode->setCurrentIndex(pStream->lenMode()); + lePktLen->setText(str.setNum(pStream->frameLen())); + lePktLenMin->setText(str.setNum(pStream->frameLenMin())); + lePktLenMax->setText(str.setNum(pStream->frameLenMax())); } // Protocols { - qDebug("ft = %d\n", pStream->proto.ft); - switch(pStream->proto.ft) + qDebug("ft = %d\n", pStream->frameType()); + switch(pStream->frameType()) { case Stream::e_ft_none: rbFtNone->setChecked(TRUE); @@ -321,6 +341,9 @@ void StreamConfigDialog::LoadCurrentStream() rbFtLlcSnap->setChecked(TRUE); break; } + +// TODO +#if 0 leDsap->setText(uintToHexStr(pStream->proto.dsap, str, 1)); leSsap->setText(uintToHexStr(pStream->proto.ssap, str, 1)); leControl->setText(uintToHexStr(pStream->proto.ctl, str, 1)); @@ -351,24 +374,23 @@ void StreamConfigDialog::LoadCurrentStream() // ... then for None/Other rbL4None->setChecked((pStream->proto.protoMask & PM_L4_PROTO_NONE) > 0); rbL4Other->setChecked((pStream->proto.protoMask & PM_L4_PROTO_OTHER) > 0); +#endif } // L2 { // L2 | Ethernet { - leDstMac->setText(uintToHexStr(pStream->l2.eth.dstMacMshw, str, 2) + - uintToHexStr(pStream->l2.eth.dstMacLsw, str, 4)); - cmbDstMacMode->setCurrentIndex(pStream->l2.eth.dstMacMode); - leDstMacCount->setText(str.setNum(pStream->l2.eth.dstMacCount)); - leDstMacStep->setText(str.setNum(pStream->l2.eth.dstMacStep)); + leDstMac->setText(uintToHexStr(pStream->mac()->dstMac(), str, 6)); + cmbDstMacMode->setCurrentIndex(pStream->mac()->dstMacMode()); + leDstMacCount->setText(str.setNum(pStream->mac()->dstMacCount())); + leDstMacStep->setText(str.setNum(pStream->mac()->dstMacStep())); - leSrcMac->setText(uintToHexStr(pStream->l2.eth.srcMacMshw, str, 2) + - uintToHexStr(pStream->l2.eth.srcMacLsw, str, 4)); - cmbSrcMacMode->setCurrentIndex(pStream->l2.eth.srcMacMode); - leSrcMacCount->setText(str.setNum(pStream->l2.eth.srcMacCount)); - leSrcMacStep->setText(str.setNum(pStream->l2.eth.srcMacStep)); - + leSrcMac->setText(uintToHexStr(pStream->mac()->srcMac(), str, 6)); + cmbSrcMacMode->setCurrentIndex(pStream->mac()->srcMacMode()); + leSrcMacCount->setText(str.setNum(pStream->mac()->srcMacCount())); + leSrcMacStep->setText(str.setNum(pStream->mac()->srcMacStep())); +#if 0 cmbCvlanPrio->setCurrentIndex(pStream->l2.eth.cvlanPrio); cmbCvlanCfi->setCurrentIndex(pStream->l2.eth.cvlanCfi); leCvlanId->setText(str.setNum(pStream->l2.eth.cvlanId)); @@ -382,43 +404,48 @@ void StreamConfigDialog::LoadCurrentStream() leSvlanTpid->setText(str.setNum(pStream->l2.eth.stpid)); cbSvlanTpidOverride->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TPID_OVERRIDE) > 0); gbSvlan->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) > 0); +#endif } } - + // L3 { // L3 | IP { - leIpVersion->setText(str.setNum(pStream->l3.ip.ver)); - cbIpVersionOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_VERSION) > 0); - leIpHdrLen->setText(str.setNum(pStream->l3.ip.hdrLen)); - cbIpHdrLenOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_HDRLEN) > 0); + leIpVersion->setText(str.setNum(pStream->ip()->ver())); + cbIpVersionOverride->setChecked( + pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideVersion)); + leIpHdrLen->setText(str.setNum(pStream->ip()->hdrLen())); + cbIpHdrLenOverride->setChecked( + pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideHdrLen)); - leIpTos->setText(uintToHexStr(pStream->l3.ip.tos, str, 1)); + leIpTos->setText(uintToHexStr(pStream->ip()->tos(), str, 1)); - leIpLength->setText(str.setNum(pStream->l3.ip.totLen)); - cbIpLengthOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_TOTLEN) > 0); + leIpLength->setText(str.setNum(pStream->ip()->totLen())); + cbIpLengthOverride->setChecked( + pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideTotLen)); - leIpId->setText(uintToHexStr(pStream->l3.ip.id, str, 2)); - leIpFragOfs->setText(str.setNum(pStream->l3.ip.fragOfs)); - cbIpFlagsDf->setChecked((pStream->l3.ip.flags & IP_FLAG_DF) > 0); - cbIpFlagsMf->setChecked((pStream->l3.ip.flags & IP_FLAG_MF) > 0); + leIpId->setText(uintToHexStr(pStream->ip()->id(), str, 2)); + leIpFragOfs->setText(str.setNum(pStream->ip()->fragOfs())); + cbIpFlagsDf->setChecked((pStream->ip()->flags() & IP_FLAG_DF) > 0); + cbIpFlagsMf->setChecked((pStream->ip()->flags() & IP_FLAG_MF) > 0); - leIpTtl->setText(str.setNum(pStream->l3.ip.ttl)); - leIpProto->setText(uintToHexStr(pStream->l3.ip.proto, str, 1)); + leIpTtl->setText(str.setNum(pStream->ip()->ttl())); + leIpProto->setText(uintToHexStr(pStream->ip()->proto(), str, 1)); - leIpCksum->setText(uintToHexStr(pStream->l3.ip.cksum, str, 2)); - cbIpCksumOverride->setChecked((pStream->l3.ip.ipMask & IM_OVERRIDE_CKSUM) > 0); + leIpCksum->setText(uintToHexStr(pStream->ip()->cksum(), str, 2)); + cbIpCksumOverride->setChecked( + pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideCksum)); - leIpSrcAddr->setText(QHostAddress(pStream->l3.ip.srcIp).toString()); - cmbIpSrcAddrMode->setCurrentIndex(pStream->l3.ip.srcIpMode); - leIpSrcAddrCount->setText(str.setNum(pStream->l3.ip.srcIpCount)); - leIpSrcAddrMask->setText(QHostAddress(pStream->l3.ip.srcIpMask).toString()); + leIpSrcAddr->setText(QHostAddress(pStream->ip()->srcIp()).toString()); + cmbIpSrcAddrMode->setCurrentIndex(pStream->ip()->srcIpMode()); + leIpSrcAddrCount->setText(str.setNum(pStream->ip()->srcIpCount())); + leIpSrcAddrMask->setText(QHostAddress(pStream->ip()->srcIpMask()).toString()); - leIpDstAddr->setText(QHostAddress(pStream->l3.ip.dstIp).toString()); - cmbIpDstAddrMode->setCurrentIndex(pStream->l3.ip.dstIpMode); - leIpDstAddrCount->setText(str.setNum(pStream->l3.ip.dstIpCount)); - leIpDstAddrMask->setText(QHostAddress(pStream->l3.ip.dstIpMask).toString()); + leIpDstAddr->setText(QHostAddress(pStream->ip()->dstIp()).toString()); + cmbIpDstAddrMode->setCurrentIndex(pStream->ip()->dstIpMode()); + leIpDstAddrCount->setText(str.setNum(pStream->ip()->dstIpCount())); + leIpDstAddrMask->setText(QHostAddress(pStream->ip()->dstIpMask()).toString()); } // L3 | ARP @@ -427,6 +454,7 @@ void StreamConfigDialog::LoadCurrentStream() } } +#if 0 // L4 { // L4 | TCP @@ -477,6 +505,7 @@ void StreamConfigDialog::LoadCurrentStream() // TODO } } +#endif } void StreamConfigDialog::StoreCurrentStream() @@ -487,30 +516,31 @@ void StreamConfigDialog::StoreCurrentStream() qDebug("storing pStream %p", pStream); +#if 1 // FIXME: Temp till we use protobuff accessors // Meta Data - pStream->meta.patternMode = (Stream::DataPatternMode) cmbPatternMode->currentIndex(); - pStream->meta.pattern = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); - - pStream->meta.lenMode = (Stream::FrameLengthMode) cmbPktLenMode->currentIndex(); - pStream->meta.frameLen = lePktLen->text().toULong(&isOk); - pStream->meta.frameLenMin = lePktLenMin->text().toULong(&isOk); - pStream->meta.frameLenMax = lePktLenMax->text().toULong(&isOk); + pStream->setPatternMode((Stream::DataPatternMode) cmbPatternMode->currentIndex()); + pStream->setPattern(lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); +#endif // Protocols { if (rbFtNone->isChecked()) - pStream->proto.ft = Stream::e_ft_none; + pStream->setFrameType(Stream::e_ft_none); else if (rbFtEthernet2->isChecked()) - pStream->proto.ft = Stream::e_ft_eth_2; + pStream->setFrameType(Stream::e_ft_eth_2); else if (rbFt802Dot3Raw->isChecked()) - pStream->proto.ft = Stream::e_ft_802_3_raw; + pStream->setFrameType(Stream::e_ft_802_3_raw); else if (rbFt802Dot3Llc->isChecked()) - pStream->proto.ft = Stream::e_ft_802_3_llc; + pStream->setFrameType(Stream::e_ft_802_3_llc); else if (rbFtLlcSnap->isChecked()) - pStream->proto.ft = Stream::e_ft_snap; - - qDebug("store ft = %d\n", pStream->proto.ft); + pStream->setFrameType(Stream::e_ft_snap); + qDebug("store ft(%d)\n", pStream->frameType()); +#if 0 pStream->proto.dsap = leDsap->text().remove(QChar(' ')).toULong(&isOk, 16); pStream->proto.ssap = leSsap->text().remove(QChar(' ')).toULong(&isOk, 16); pStream->proto.ctl = leControl->text().remove(QChar(' ')).toULong(&isOk, 16); @@ -531,24 +561,33 @@ void StreamConfigDialog::StoreCurrentStream() pStream->proto.protoMask |= PM_L4_PROTO_NONE; else if (rbL4Other->isChecked()) pStream->proto.protoMask |= PM_L4_PROTO_OTHER; +#endif } // L2 { // L2 | Ethernet { - pStream->l2.eth.dstMacMshw = leDstMac->text().remove(QChar(' ')).left(4).toULong(&isOk, 16); - pStream->l2.eth.dstMacLsw = leDstMac->text().remove(QChar(' ')).right(8).toULong(&isOk, 16); - pStream->l2.eth.dstMacMode = (Stream::MacAddrMode) cmbDstMacMode->currentIndex(); - pStream->l2.eth.dstMacCount = leDstMacCount->text().toULong(&isOk); - pStream->l2.eth.dstMacStep = leDstMacStep->text().toULong(&isOk); + pStream->mac()->setDstMac( + leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); + pStream->mac()->setDstMacMode( + (MacProtocol::MacAddrMode) cmbDstMacMode->currentIndex()); + pStream->mac()->setDstMacCount( + leDstMacCount->text().toULong(&isOk)); + pStream->mac()->setDstMacStep( + leDstMacStep->text().toULong(&isOk)); - pStream->l2.eth.srcMacMshw = leSrcMac->text().remove(QChar(' ')).left(4).toULong(&isOk, 16); - pStream->l2.eth.srcMacLsw = leSrcMac->text().remove(QChar(' ')).right(8).toULong(&isOk, 16); - pStream->l2.eth.srcMacMode = (Stream::MacAddrMode) cmbSrcMacMode->currentIndex(); - pStream->l2.eth.srcMacCount = leSrcMacCount->text().toULong(&isOk); - pStream->l2.eth.srcMacStep = leSrcMacStep->text().toULong(&isOk); + pStream->mac()->setSrcMac( + leSrcMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); + pStream->mac()->setSrcMacMode( + (MacProtocol::MacAddrMode) cmbSrcMacMode->currentIndex()); + pStream->mac()->setSrcMacCount( + leSrcMacCount->text().toULong(&isOk)); + pStream->mac()->setSrcMacStep( + leSrcMacStep->text().toULong(&isOk)); + +#if 0 pStream->l2.eth.vlanMask = 0; pStream->l2.eth.cvlanPrio = cmbCvlanPrio->currentIndex(); @@ -568,6 +607,7 @@ void StreamConfigDialog::StoreCurrentStream() pStream->l2.eth.vlanMask |= VM_SVLAN_TPID_OVERRIDE; if (gbSvlan->isChecked()) pStream->l2.eth.vlanMask |= VM_SVLAN_TAGGED; +#endif } } @@ -575,42 +615,48 @@ void StreamConfigDialog::StoreCurrentStream() { // L3 | IP { - pStream->l3.ip.ipMask = 0; + IpProtocol *ip = pStream->ip(); + IpProtocol::IpFlags f; - pStream->l3.ip.ver = leIpVersion->text().toULong(&isOk); + ip->setVer(leIpVersion->text().toULong(&isOk)); if (cbIpVersionOverride->isChecked()) - pStream->l3.ip.ipMask |= IM_OVERRIDE_VERSION; - pStream->l3.ip.hdrLen = leIpHdrLen->text().toULong(&isOk); + f |= IpProtocol::IpOverrideVersion; + ip->setHdrLen(leIpHdrLen->text().toULong(&isOk)); if (cbIpHdrLenOverride->isChecked()) - pStream->l3.ip.ipMask |= IM_OVERRIDE_HDRLEN; + f |= IpProtocol::IpOverrideHdrLen; - pStream->l3.ip.tos = leIpTos->text().toULong(&isOk, 16); + ip->setTos(leIpTos->text().toULong(&isOk, 16)); - pStream->l3.ip.totLen = leIpLength->text().toULong(&isOk); + ip->setTotLen(leIpLength->text().toULong(&isOk)); if (cbIpLengthOverride->isChecked()) - pStream->l3.ip.ipMask |= IM_OVERRIDE_TOTLEN; + f |= IpProtocol::IpOverrideHdrLen; - pStream->l3.ip.id = leIpId->text().remove(QChar(' ')).toULong(&isOk, 16); - pStream->l3.ip.fragOfs = leIpFragOfs->text().toULong(&isOk); - if (cbIpFlagsDf->isChecked()) pStream->l3.ip.ipMask |= IP_FLAG_DF; - if (cbIpFlagsMf->isChecked()) pStream->l3.ip.ipMask |= IP_FLAG_MF; + ip->setId(leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + ip->setFragOfs(leIpFragOfs->text().toULong(&isOk)); - pStream->l3.ip.ttl = leIpTtl->text().toULong(&isOk); - pStream->l3.ip.proto = leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16); + int ff; + if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + ip->setFlags(ff); + + ip->setTtl(leIpTtl->text().toULong(&isOk)); + ip->setProto(leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->l3.ip.cksum = leIpCksum->text().remove(QChar(' ')).toULong(&isOk); + ip->setCksum(leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); if (cbIpCksumOverride->isChecked()) - pStream->l3.ip.ipMask |= IM_OVERRIDE_CKSUM; + f |= IpProtocol::IpOverrideCksum; - pStream->l3.ip.srcIp = QHostAddress(leIpSrcAddr->text()).toIPv4Address(); - pStream->l3.ip.srcIpMode = (Stream::IpAddrMode) cmbIpSrcAddrMode->currentIndex(); - pStream->l3.ip.srcIpCount = leIpSrcAddrCount->text().toULong(&isOk); - pStream->l3.ip.srcIpMask = QHostAddress(leIpSrcAddrMask->text()).toIPv4Address(); + ip->setSrcIp(QHostAddress(leIpSrcAddr->text()).toIPv4Address()); + ip->setSrcIpMode((IpProtocol::IpAddrMode) cmbIpSrcAddrMode->currentIndex()); + ip->setSrcIpCount(leIpSrcAddrCount->text().toULong(&isOk)); + ip->setSrcIpMask(QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); - pStream->l3.ip.dstIp = QHostAddress(leIpDstAddr->text()).toIPv4Address(); - pStream->l3.ip.dstIpMode = (Stream::IpAddrMode) cmbIpDstAddrMode->currentIndex(); - pStream->l3.ip.dstIpCount = leIpDstAddrCount->text().toULong(&isOk); - pStream->l3.ip.dstIpMask = QHostAddress(leIpDstAddrMask->text()).toIPv4Address(); + ip->setDstIp(QHostAddress(leIpDstAddr->text()).toIPv4Address()); + ip->setDstIpMode((IpProtocol::IpAddrMode) cmbIpDstAddrMode->currentIndex()); + ip->setDstIpCount(leIpDstAddrCount->text().toULong(&isOk)); + ip->setDstIpMask(QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); + + ip->setIpFlags(f); } // L3 | ARP @@ -619,6 +665,8 @@ void StreamConfigDialog::StoreCurrentStream() } } +// TODO +#if 0 // L4 { // L4 | TCP @@ -676,6 +724,7 @@ void StreamConfigDialog::StoreCurrentStream() // TODO } } +#endif } void StreamConfigDialog::on_pbOk_clicked() { diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 6ccf074..1d3c99b 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -37,6 +37,7 @@ private: private slots: void on_cmbDstMacMode_currentIndexChanged(QString mode); + void on_cmbSrcMacMode_currentIndexChanged(QString mode); void on_pbPrev_clicked(); void on_pbNext_clicked(); @@ -56,7 +57,7 @@ private slots: void on_pbOk_clicked(); }; -QString & uintToHexStr(quint32 num, QString &hexStr, quint8 octets); +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); #endif diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 7e7efe1..a9c1ba4 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -33,7 +33,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 2 + 0 @@ -191,7 +191,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 2 + 1 @@ -2145,12 +2145,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 147 - 238 + 252 + 288 - 147 - 238 + 252 + 317 @@ -2161,12 +2161,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 147 - 238 + 252 + 388 - 147 - 238 + 252 + 417 @@ -2177,12 +2177,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 259 - 101 - 238 + 41 + 317 @@ -2193,12 +2193,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 259 - 101 - 238 + 81 + 317 @@ -2209,12 +2209,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 259 - 133 - 238 + 120 + 317 @@ -2225,12 +2225,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 259 - 147 - 238 + 252 + 288 @@ -2241,12 +2241,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 359 - 101 - 238 + 41 + 417 @@ -2257,12 +2257,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 359 - 101 - 238 + 120 + 417 @@ -2273,12 +2273,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 359 - 147 - 238 + 252 + 388 @@ -2289,12 +2289,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 238 + 31 + 359 - 101 - 238 + 81 + 417 @@ -2305,12 +2305,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 276 + 123 + 305 - 101 - 276 + 123 + 305 @@ -2321,12 +2321,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 276 + 123 + 305 - 101 - 276 + 123 + 305 @@ -2401,12 +2401,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 276 + 123 + 305 - 101 - 276 + 123 + 305 @@ -2417,12 +2417,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 101 - 276 + 123 + 305 - 101 - 276 + 123 + 305 @@ -3121,12 +3121,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 224 - 207 + 381 + 140 - 224 - 209 + 381 + 174 @@ -3137,12 +3137,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 125 - 207 + 30 + 69 - 191 - 236 + 85 + 285 @@ -3153,12 +3153,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 125 - 207 + 30 + 94 - 173 - 236 + 67 + 331 @@ -3169,12 +3169,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 125 - 207 + 30 + 94 - 224 - 216 + 222 + 189 diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 21588b7..d56f4ee 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -125,7 +125,7 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r return true; break; case StreamStatus: - mCurrentPort->mStreams[index.row()].setEnabled(value.toBool()); + mCurrentPort->mStreams[index.row()].setIsEnabled(value.toBool()); return true; break; diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 0000000..7c95f2b --- /dev/null +++ b/common/Makefile @@ -0,0 +1,14 @@ +RM=del +PROTOC=protoc + +#-#-# + +all: protocol.pb.cc + +protocol.pb.cc: protocol.proto + $(PROTOC) --cpp_out=. $< + +clean: + $(RM) *.pb.h *.pb.cc + +distclean: clean diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..4fa777d --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,302 @@ +// stream.proto + +// FIXME: Re-evaluate Tag Values + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint32 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +message Llc { + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +message Snap { + optional uint32 oui = 1; + optional uint32 type = 2; +} + +message Eth2 { + optional uint32 type = 1; +} + +message Vlan { + // VLAN presence/absence + optional bool is_cvlan_tagged = 9; + optional bool is_ctpid_override = 10; + optional bool is_svlan_tagged = 11; + optional bool is_stpid_override = 12; + + // VLAN values + optional uint32 ctpid = 13; + optional uint32 cvlan_tag = 14; // includes prio, cfi and cvlanid + optional uint32 stpid = 15; + optional uint32 svlan_tag = 16; // includes pcp, de and svlanid +} + +// IP +message Ip { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 tot_len = 7; + optional uint32 id = 8 [default = 1234]; + // TODO: rename flags to frag_flags + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFFFF]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFFFF]; + + // TODO: Options +} + +message Arp { +// TODO: ARP +} + +message Tcp { + + optional bool is_override_hdrlen = 1; + optional bool is_override_cksum = 2; + + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; + + optional uint32 seq_num = 5 [default = 129018]; + optional uint32 ack_num = 6 [default = 98223]; + + optional uint32 hdrlen_rsvd = 7 [default = 0x50]; + optional uint32 flags = 8; + + optional uint32 window = 9 [default = 1024]; + optional uint32 cksum = 10; + optional uint32 urg_ptr = 11; +} + +// UDP +message Udp { + optional bool is_override_totlen = 1; + optional bool is_override_cksum = 2; + + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; + optional uint32 totLen = 5; + optional uint32 cksum = 6; +} + +// TODO: ICMP +message Icmp { +} + +// TODO: IGMP +message Igmp { +} + + +message StreamCore { + + enum FrameType { + e_ft_none = 0; + e_ft_eth_2 = 1; + e_ft_802_3_raw = 2; + e_ft_802_3_llc = 3; + e_ft_snap = 4; + } + + enum L3Proto { + e_l3_none = 0; + e_l3_other = 1; + e_l3_ip = 2; + e_l3_arp = 3; + } + + enum L4Proto { + e_l4_none = 0; + e_l4_other = 1; + e_l4_tcp = 2; + e_l4_udp = 3; + e_l4_icmp = 4; + e_l4_igmp = 5; + } + + enum DataPatternMode { + e_dp_fixed = 0; + e_dp_inc = 1; + e_dp_dec = 2; + e_dp_random = 3; + } + + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Data Pattern + optional DataPatternMode pattern_mode = 11; + optional uint32 pattern = 12; + optional uint32 data_start_ofs = 13; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14; + optional uint32 frame_len = 15; + optional uint32 frame_len_min = 16; + optional uint32 frame_len_max = 17; + + // Currently Selected Protocols + optional FrameType ft = 21 [default = e_ft_none]; + optional L3Proto l3_proto = 22; + optional L4Proto l4_proto = 23; +} + +message StreamId { + required uint32 port_id = 1; + required uint32 stream_id = 2; +} + +message Stream { + + required StreamId id = 1; + optional StreamCore core = 2; + + // Protocol data - L2 + optional Mac mac = 51; + optional Llc llc = 52; + optional Snap snap = 53; + optional Eth2 eth2 = 54; + + // Protocol data - L3 + optional Ip ip = 61; + optional Arp arp = 62; + + // Protocol data - L4 + optional Tcp tcp = 71; + optional Udp udp = 72; + optional Icmp icmp = 73; + optional Igmp igmp = 74; + +} + +message Void { + // nothing! +} + +message Ack { + // TODO +} + +message PortIdList { + repeated uint32 port_id = 1; +} + +message StreamIdList { + repeated StreamId id = 1; +} + + +message PortConfig { + required uint32 port_id = 1; + optional string name = 2; + optional string description = 3; + optional bool is_enabled = 4; + optional bool is_oper_up = 5; + optional bool is_exclusive_control = 6; +} + +message PortConfigList { + repeated PortConfig list = 1; +} + +message StreamConfigList { + repeated Stream stream = 1; +} + +message CaptureBuffer { + // TODO +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +message PortStats { + // TODO +} + +message PortStatsList { + repeated PortStats list = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + + rpc getStreamIdList(PortIdList) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortIdList) returns (CaptureBufferList); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..19e3daf --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,66 @@ +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +class PbHelper +{ +public: + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + qDebug("In %s", __FUNCTION__); + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + + return true; + + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; + } + +}; +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..79c0e08 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +CONFIG += qt +QT += network +DEFINES += HAVE_REMOTE +INCLUDEPATH += "c:\msys\1.0\local\include" +LIBS += -L"C:\msys\1.0\local\lib" -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp +client.path = ..\client\debug +client.files = debug\libpbrpc.a debug\pbrpc.dll +server.path = ..\server\debug +server.files = debug\libpbrpc.a debug\pbrpc.dll +INSTALLS += client server diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..0d6226b --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,189 @@ +#include "pbrpcchannel.h" + +//#include "../common/protocol.pb.h" + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msg[1024]; // FIXME: hardcoding + char *p = (char *)&msg; + int len; + + qDebug("In %s", __FUNCTION__); + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_REQUEST); // type + qDebug("CLi:GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), + PB_MSG_TYPE_REQUEST); + *((quint16*)(p+2)) = HTONS(method->index()); // method id + // (p+4) len later after serialization + *((quint16*)(p+6)) = HTONS(0); // rsvd + + // SerialData is at offset 8 + req->SerializeToArray((void*) (p+8), sizeof(msg)); + + len = req->ByteSize(); + *((quint16*)(p+4)) = HTONS(len); // len + + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, len+8, + req->ShortDebugString().c_str()); + BUFDUMP(msg, len+8); + + mpSocket->write(msg, len + 8); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + char msg[1024]; // FIXME: hardcoding; + char *p = (char*)&msg; + int msgLen; + quint16 type, method, len, rsvd; + PbRpcController *controller; + + qDebug("In %s", __FUNCTION__); + + msgLen = mpSocket->read(msg, sizeof(msg)); + + qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + BUFDUMP(msg, msgLen); + + type = NTOHS(GET16(p+0)); + method = NTOHS(GET16(p+2)); + len = NTOHS(GET16(p+4)); + rsvd = NTOHS(GET16(p+6)); + + if (!isPending) + { + qDebug("not waiting for response"); + + goto _error_exit; + } + + if (type != PB_MSG_TYPE_RESPONSE) + { + qDebug("invalid msgType %d (expected = %d)", type, + PB_MSG_TYPE_RESPONSE); + + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + + goto _error_exit; + } + + + // Serialized data starts from offset 8 + response->ParseFromArray((void*) &msg[8], len); + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + + + pendingMethodId = -1; + controller = NULL; + //done = NULL; + response = NULL; + isPending = false; + + done->Run(); + + return; + +_error_exit: + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..657b095 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,74 @@ +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + // + // TODO(?): change controller, done and response to references + // instead of pointers? + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..5407602 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,52 @@ +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// FIXME: check which one is right - wrong one seems to be working!!!!! +#if 0 +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+0) << 8 ) \ + | (*((quint8*)(p)+1))) +#else +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) +#endif + +#define BYTESWAP4(x) \ + (((x & 0xFF000000) >> 24) | \ + ((x & 0x00FF0000) >> 8) | \ + ((x & 0x0000FF00) << 8) | \ + ((x & 0x000000FF) << 24)) + +#define BYTESWAP2(x) \ + (((x & 0xFF00) >> 8) | \ + ((x & 0x00FF) << 8)) + +// TODO: portability +#if 1 +#define HTONL(x) BYTESWAP4(x) +#define NTOHL(x) BYTESWAP4(x) +#define HTONS(x) BYTESWAP2(x) +#define NTOHS(x) BYTESWAP2(x) +#else +#define HTONL(x) (x) +#define NTOHL(x) (x) +#define HTONS(x) (x) +#define NTOHS(x) (x) +#endif + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (2) [not including this header] +** - RSVD (2) +*/ +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..0b67ded --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,27 @@ +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class PbRpcController : public ::google::protobuf::RpcController +{ + bool failed; + std::string errStr; + +public: + PbRpcController() { failed = false; } + + // Client Side Methods + void Reset() { failed=false;} + bool Failed() const { return failed; } + void StartCancel() { /* TODO */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure *callback) { /*TODO*/ } +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..45555bf --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,180 @@ +#include "pbhelper.h" +#include "rpcserver.h" + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + LogInt(tr("Unable to start the server: %1").arg(server->errorString())); + return false; + } + + LogInt(tr("The server is running on %1:%2").arg(server->serverAddress().toString()).arg(server->serverPort())); + return true; +} + +void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) +{ + char msg[1024]; // FIXME: hardcoding + char *p = (char *)&msg; + int len; + + qDebug("In RpcServer::done"); + + // TODO: check PbRpcController to see if method failed + if (PbRpcController->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_RESPONSE); // type TODO:RESPONSE + *((quint16*)(p+2)) = HTONS(pendingMethodId); // method + *((quint16*)(p+6)) = HTONS(0); // rsvd + + // SerialData is at offset 8 + resp->SerializeToArray((void*) (p+8), sizeof(msg)); + + len = resp->ByteSize(); + (*(quint16*)(p+4)) = HTONS(len); // len + + qDebug("Server(%s): sending %d bytest to client encoding <%s>", + __FUNCTION__, len + 8, resp->DebugString().c_str()); + BUFDUMP(msg, len + 8); + + clientSock->write(msg, len + 8); + +_exit: + delete PbRpcController; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + LogInt(tr("already connected, no new connections will be accepted\n")); + + // Accept and close connection + // TODO: Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + LogInt(tr("accepting new connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + LogInt(tr("connection closed from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + LogInt(clientSock->errorString()); +} + +void RpcServer::when_dataAvail() +{ + char msg[1024]; // FIXME: hardcoding; + int msgLen; + char *p = (char*) &msg; + quint16 type, method, len, rsvd; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + msgLen = clientSock->read(msg, sizeof(msg)); + LogInt(QString(QByteArray(msg, msgLen).toHex())); + + qDebug("Server %s: rcvd %d bytes", __FUNCTION__, msgLen); + BUFDUMP(msg, msgLen); + + type = NTOHS(GET16(p+0)); + qDebug("GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), type); + method = NTOHS(GET16(p+2)); + len = NTOHS(GET16(p+4)); + rsvd = NTOHS(GET16(p+6)); + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; // TODO: Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; // TODO: Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + // Serialized data starts from offset 8 + req->ParseFromArray((void*) (msg+8), len); + + controller = new PbRpcController; + + service->CallMethod(methodDesc, controller, req, resp, + NewCallback(this, &RpcServer::done, resp, controller)); + + return; + +_error_exit: + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..9e21587 --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,44 @@ +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + + bool isPending; + int pendingMethodId; + + void LogInt (QString log) {qDebug("%s", log.toAscii().data());} + +public: + RpcServer(); // TODO: use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + void done(::google::protobuf::Message *resp, PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstracthost.h b/server/abstracthost.h index 38e266c..862666c 100644 --- a/server/abstracthost.h +++ b/server/abstracthost.h @@ -5,7 +5,9 @@ class AbstractHost { public: virtual void Log(const char* str) = 0; +#if 0 // PB virtual int SendMsg(const void* msg, int size) = 0; +#endif }; #endif diff --git a/server/drone.cpp b/server/drone.cpp index 54bf9cf..98300ee 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -6,13 +6,19 @@ Drone::Drone(QDialog *parent) : QDialog(parent) { ui.setupUi(this); +#if 0 // PB + rxtx = new RxTx(this); +#endif + rpcServer = new RpcServer(); + service = new MyService(this); + rpcServer->registerService(service, myport?myport:7878); + +#if 0 // PB serverPortNum = DRONE_PORT; clientSock = NULL; if (myport) - serverPortNum = myport; - - rxtx = new RxTx(this); + serverPortNum = myport); server = new QTcpServer(this); connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); @@ -21,6 +27,7 @@ Drone::Drone(QDialog *parent) LogInt(tr("Unable to start the server: %1").arg(server->errorString())); else LogInt(tr("The server is running on %1:%2").arg(server->serverAddress().toString()).arg(server->serverPort())); +#endif } void Drone::Log(const char* str) @@ -28,17 +35,20 @@ void Drone::Log(const char* str) ui.teLog->append(QString(str)); } +#if 0 // PB int Drone::SendMsg(const void* msg, int size) { qDebug("Inside SendMsg\n"); clientSock->write((char*) msg, size); } +#endif void Drone::LogInt(const QString &str) { ui.teLog->append(str); } +#if 0 // PB void Drone::when_newConnection() { if (clientSock) @@ -83,3 +93,4 @@ void Drone::when_error(QAbstractSocket::SocketError socketError) { LogInt(clientSock->errorString()); } +#endif diff --git a/server/drone.h b/server/drone.h index f99255b..3c4adba 100644 --- a/server/drone.h +++ b/server/drone.h @@ -5,7 +5,11 @@ #include #include "ui_drone.h" #include "abstracthost.h" +#if 0 // PB #include "rxtx.h" +#endif +#include "rpcserver.h" +#include "myservice.h" class Drone : public QDialog, AbstractHost @@ -13,24 +17,31 @@ class Drone : public QDialog, AbstractHost Q_OBJECT public: + Ui::Drone ui; Drone(QDialog *parent = 0); void Log(const char *msg); +#if 0 // PB int SendMsg(const void* msg, int msgLen); +#endif - Ui::Drone ui; private: +#if 0 // PB RxTx *rxtx; +#endif + RpcServer *rpcServer; + OstProto::OstService *service; + void LogInt(const QString &msg); +#if 0 // PB QTcpServer *server; QTcpSocket *clientSock; #define DRONE_PORT 7878 quint16 serverPortNum; - // Ui::Drone ui; - void LogInt(const QString &msg); + private slots: void when_newConnection(); void when_disconnected(); void when_dataAvail(); void when_error(QAbstractSocket::SocketError socketError); +#endif }; #endif - diff --git a/server/drone.pro b/server/drone.pro index 1fd8862..df79396 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -2,8 +2,14 @@ TEMPLATE = app CONFIG += qt QT += network DEFINES += HAVE_REMOTE +INCLUDEPATH += "c:\msys\1.0\local\include" INCLUDEPATH += "C:\DevelLibs\WpdPack\Include" +INCLUDEPATH += "..\rpc" LIBS += -L"C:\DevelLibs\WpdPack\Lib" -lwpcap -HEADERS += drone.h +LIBS += -L"..\rpc\debug" -lpbrpc +HEADERS += drone.h FORMS += drone.ui -SOURCES += drone_main.cpp drone.cpp rxtx.cpp +SOURCES += drone_main.cpp drone.cpp +SOURCES += myservice.cpp + +SOURCES += "..\common\protocol.pb.cc" diff --git a/server/myservice.cpp b/server/myservice.cpp new file mode 100644 index 0000000..93e625c --- /dev/null +++ b/server/myservice.cpp @@ -0,0 +1,290 @@ +#include "myservice.h" +#include "qdebug.h" + +#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} + +MyService::MyService(AbstractHost *host) +{ + pcap_if_t *dev; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + + // Init Data + this->host = host; + numPorts = 0; + alldevs = NULL; + + LOG("Retrieving the device list from the local machine\n"); + if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) + { + LOG("Error in pcap_findalldevs_ex: %s\n", errbuf); + goto _fail; + } + + /* Count number of local ports */ + for(dev = alldevs; dev != NULL; dev = dev->next) + numPorts++; + + portInfo = new PortInfo[numPorts]; + + /* Populate and Print the list */ + for(i=0, dev=alldevs; dev!=NULL; i++, dev=dev->next) + { +#if 0 // PB + //portInfo[i].portId = i; + //portInfo[i].dev = dev; + //portInfo[i].streamHead = NULL; + //portInfo[i].streamTail = NULL; +#endif + portInfo[i].setId(i); + portInfo[i].setPcapDev(dev); +#if 1 + LOG("%d. %s", i, dev->name); + if (dev->description) + { + LOG(" (%s)\n", dev->description); + } + else + LOG(" (No description available)\n"); +#endif + } + + if (i == 0) + { + LOG("\nNo interfaces found! Make sure WinPcap is installed.\n"); + goto _fail; + } + +_fail: + return; +} + +MyService::~MyService() +{ + unsigned int i; +#if 0 // PB????? + for (i = 0; i < numPorts; i++) + DeleteAllStreams(i); +#endif + pcap_freealldevs(alldevs); +} + +void MyService::getPortIdList( + ::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + + for (uint i = 0; i < numPorts; i++) + response->add_port_id(portInfo[i].d.port_id()); + + qDebug("Server(%s): portid count = %d", __FUNCTION__, response->port_id_size()); + + qDebug("Server(%s): %s", __FUNCTION__, response->DebugString().c_str()); + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::PortConfigList* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + + for (int i=0; i < request->port_id_size(); i++) + { + unsigned int id; + + id = request->port_id(i); + if (id < numPorts) + { + OstProto::PortConfig *p; + + p = response->add_list(); + p->CopyFrom(portInfo[request->port_id(i)].d); + } + } + + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::StreamIdList* response, +::google::protobuf::Closure* done) +{ + + for (int i = 0; i < request->port_id_size(); i++) + { + unsigned int portId; + + portId = request->port_id(i); + if (portId >= numPorts) + { + qDebug("%s: Invalid port id %d", __FUNCTION__, portId); + continue; // TODO: Partial status of RPC + } + + for (int j = 0; j < portInfo[portId].streamList.size(); j++) + { + OstProto::StreamId *s, *q; + + q = portInfo[portId].streamList[j].d.mutable_id(); + assert(q->port_id() == portId); + + s = response->add_id(); + s->set_port_id(portId); + s->set_stream_id(q->stream_id()); + } + } + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, +const ::OstProto::StreamIdList* request, +::OstProto::StreamConfigList* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + + for (int i = 0; i < request->id_size(); i++) + { + unsigned int portId; + unsigned int streamId; + + portId = request->id(i).port_id(); + if (portId >= numPorts) + { + qDebug("%s: Invalid port id %d", __FUNCTION__, portId); + continue; // TODO: Partial status of RPC + } + + streamId = request->id(i).stream_id(); + if (streamId >= numPorts) + { + qDebug("%s: Invalid port id %d", __FUNCTION__, portId); + continue; // TODO: Partial status of RPC + } + + for (int j = 0; j < portInfo[portId].streamList.size(); j++) + { + OstProto::Stream *s, *q; + +#if 0 + q = portInfo[portId].streamList[j].d.e_stream(); + assert(q->port_id() == portId); + + s = response->add_stream(); + s->set_port_id(portId); + s->set_stream_id(q->stream_id()); +#endif + // TODO: more params + } + } + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, +const ::OstProto::StreamIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, +const ::OstProto::StreamIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, +const ::OstProto::StreamConfigList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::CaptureBufferList* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::PortStatsList* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* controller, +const ::OstProto::PortIdList* request, +::OstProto::Ack* response, +::google::protobuf::Closure* done) +{ + qDebug("In %s", __FUNCTION__); + controller->SetFailed("Not Implemented"); + done->Run(); +} + diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..83ebc0f --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,146 @@ +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + + +#if 0 +#include +#include +#endif + +#include "../common/protocol.pb.h" +#include "abstracthost.h" +#include +#include + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class MyService; + +class StreamInfo +{ + friend class MyService; + + OstProto::Stream d; + +#if 0 // PB + 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; +#endif +}; + + +class PortInfo +{ + friend class MyService; + + OstProto::PortConfig d; + pcap_if_t *dev; + + QList streamList; + +#if 0 // PB + unsigned int portId; // FIXME:need? + Stream *streamHead; + Stream *streamTail; +#endif + +public: + // TODO(LOW): Both setId and setPcapDev() should together form the ctor + void setId(unsigned int id) { d.set_port_id(id); } + void setPcapDev(pcap_if_t *dev) + { + this->dev = dev; + d.set_name("eth"); // FIXME: suffix portid + d.set_description(dev->description); + d.set_is_enabled(true); // FIXME:check + d.set_is_oper_up(true); // FIXME:check + d.set_is_exclusive_control(false); // FIXME: check + } +}; + +class MyService: public OstProto::OstService +{ + AbstractHost *host; + char logStr[1024]; + + unsigned numPorts; + PortInfo *portInfo; + pcap_if_t *alldevs; + +public: + MyService(AbstractHost* host); + virtual ~MyService(); + + //static const ::google::protobuf::ServiceDescriptor* descriptor(); + + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::CaptureBufferList* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); +}; + +#endif diff --git a/server/rxtx.cpp b/server/rxtx.cpp index 2eab59b..f4b5308 100644 --- a/server/rxtx.cpp +++ b/server/rxtx.cpp @@ -1,6 +1,12 @@ + +File Not used anymore + +#if 0 #include "qtglobal" // FIXME: needed only for qdebug #include "rxtx.h" +#if 0 // PB #include "../common/protocol.h" +#endif @@ -157,7 +163,9 @@ void RxTx::SendCapabilityInfo(void) } host->Log("Sending msg\n"); host->Log(logStr); +#if 0 // PB host->SendMsg(pktBuff, msgLen); +#endif } void RxTx::ProcessPortConfig(const char* msg, int len) @@ -503,4 +511,4 @@ Stream* RxTx::GetStream(unsigned int port, unsigned int streamId) return NULL; } - +#endif diff --git a/server/rxtx.h b/server/rxtx.h index 986b978..fac9331 100644 --- a/server/rxtx.h +++ b/server/rxtx.h @@ -1,9 +1,16 @@ + +File not used anymore + +#if 0 + #ifndef _RXTX_H #define _RXTX_H #include "pcap.h" #include "abstracthost.h" +#include "../common/protocol.h" + #define GET16(x) (UINT16)( \ (*((UINT8*)x+0) << 16 ) \ | (*((UINT8*)x+1))) @@ -74,3 +81,4 @@ class RxTx }; #endif +#endif From c7f4c1dec9e776d5261912a8f0f7deb18ae172e7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 24 Aug 2008 04:39:08 +0000 Subject: [PATCH 007/294] - StreamModel no longer a friend of Stream - PacketModel refactored by moving protocol specific stuff into Stream and xxxProtocol classes --- Makefile | 9 + client/packetmodel.cpp | 206 +++++--- client/packetmodel.h | 12 +- client/port.cpp | 211 +++++--- client/port.h | 65 ++- client/portgroup.cpp | 413 ++++++++++----- client/portgroup.h | 13 +- client/portgrouplist.h | 1 + client/portstatsmodel.cpp | 3 +- client/portswindow.cpp | 44 +- client/stream.cpp | 953 +++++++++++++++++++++++++++++----- client/stream.h | 508 ++++++++++++++---- client/streamconfigdialog.cpp | 342 +++++++----- client/streamconfigdialog.h | 9 +- client/streamconfigdialog.ui | 5 +- client/streammodel.cpp | 17 +- client/streammodel.h | 2 + common/protocol.proto | 43 +- rpc/pbhelper.h | 92 +++- rpc/pbrpcchannel.cpp | 23 +- rpc/rpcserver.cpp | 33 +- server/myservice.cpp | 278 ++++++---- server/myservice.h | 129 ++--- server/rxtx.cpp | 2 +- server/rxtx.h | 2 +- 25 files changed, 2528 insertions(+), 887 deletions(-) diff --git a/Makefile b/Makefile index c24ac11..8b555c4 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,12 @@ all: $(MAKE) -C common $(MAKE) -C server $(MAKE) -C client + +clean: + $(MAKE) -C rpc $@ + $(MAKE) -C common $@ + $(MAKE) -C server $@ + $(MAKE) -C client $@ + +qmake: + for %%d in (rpc common server client) cd %%d; qmake; cd..; diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 8711b53..5800413 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -4,8 +4,11 @@ PacketModel::PacketModel(Stream *pStream, QObject *parent) { mpStream = pStream; +#ifdef NEW_IMPL +// Nothing else +#else populatePacketProtocols(); -#if 1 + registerFrameTypeProto(); registerVlanProto(); registerIpProto(); @@ -23,17 +26,25 @@ int PacketModel::rowCount(const QModelIndex &parent) const { IndexId parentId; - // Parent - Invalid i.e. Invisible Root. - // Children - Protocol (Top Level) Items + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items if (!parent.isValid()) +#ifdef NEW_IMPL + return mpStream->numProtocols(); +#else return protoCount(); +#endif // Parent - Valid Item parentId.w = parent.internalId(); switch(parentId.ws.type) { case ITYP_PROTOCOL: +#ifdef NEW_IMPL + return mpStream->protocol(parentId.ws.protocol)->numFields(); +#else return fieldCount(parentId.ws.protocol); +#endif case ITYP_FIELD: return 0; default: @@ -126,7 +137,11 @@ _exit: QVariant PacketModel::data(const QModelIndex &index, int role) const { IndexId id; +#ifdef NEW_IMPL +// Nothing +#else ProtocolInfo proto; +#endif if (!index.isValid()) return QVariant(); @@ -138,12 +153,24 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const switch(id.ws.type) { case ITYP_PROTOCOL: +#ifdef NEW_IMPL + return QString("%1 (%2)") + .arg(mpStream->protocol(id.ws.protocol)->protocolShortName()) + .arg(mpStream->protocol(id.ws.protocol)->protocolName()); +#else return protoName(id.ws.protocol); +#endif case ITYP_FIELD: +#ifdef NEW_IMPL + return mpStream->protocol(id.ws.protocol)->fieldName(index.row()) + + QString(" : ") + + mpStream->protocol(id.ws.protocol)->fieldTextValue(index.row()); +#else return fieldName(id.ws.protocol, index.row()) + QString(" : ") + fieldTextValue(id.ws.protocol, index.row()).toString(); +#endif default: qWarning("%s: Unhandled ItemType", __FUNCTION__); @@ -154,12 +181,29 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const return QVariant(); } - +#ifdef NEW_IMPL +// required methods all part of the Stream class +#else /* ** --------------- Private Stuff ----------------- ** FIXME(MED): Move these to the Stream Class ** */ + +/*! + Looking at the stream's protocols and populate an ordered list of + protocols accordingly. The order of protocols will be in the order of + protocol headers viz. + + - None/Eth2/802.3 (Mac Addr) + - LLC + - SNAP + - SVLAN + - CVLAN + - L3 (IP/ARP) + - L4 (TCP/UDP/ICMP/IGMP) + +*/ void PacketModel::populatePacketProtocols() { int proto; @@ -167,9 +211,8 @@ void PacketModel::populatePacketProtocols() // Clear the protocols list mPacketProtocols.clear(); -#if 0 // FIXME: protobuf // Check and populate L2 Protocol - switch(mpStream->proto.ft) + switch(mpStream->frameType()) { case Stream::e_ft_none: proto = PTYP_L2_NONE; @@ -195,71 +238,80 @@ void PacketModel::populatePacketProtocols() break; default: - qDebug("%s: Unsupported frametype", __FUNCTION__); + qDebug("%s: Unsupported frametype %d", __FUNCTION__, + mpStream->frameType()); proto = PTYP_INVALID; } mPacketProtocols.append(proto); // Check and populate VLANs, if present - if (mpStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) + if (mpStream->vlan()->vlanFlags().testFlag(VlanProtocol::VlanSvlanTagged)) mPacketProtocols.append(PTYP_SVLAN); - if (mpStream->l2.eth.vlanMask & VM_CVLAN_TAGGED) + if (mpStream->vlan()->vlanFlags().testFlag(VlanProtocol::VlanCvlanTagged)) mPacketProtocols.append(PTYP_CVLAN); // Check and populate L3 protocols - if (mpStream->proto.protoMask & PM_L3_PROTO_NONE) - goto _data; - - switch(mpStream->proto.etherType) + switch (mpStream->l3Proto()) { - case ETH_TYP_IP: + case Stream::e_l3_none : + goto _data; + break; + + case Stream::e_l3_ip : proto = PTYP_L3_IP; break; - case ETH_TYP_ARP: + case Stream::e_l3_arp: proto = PTYP_L3_ARP; break; default: - qDebug("%s: Unsupported ethtype", __FUNCTION__); + qDebug("%s: Unsupported L3 Proto %d", __FUNCTION__, + mpStream->l3Proto()); proto = PTYP_INVALID; } mPacketProtocols.append(proto); - if (mpStream->proto.protoMask & PM_L4_PROTO_NONE) - goto _data; - - switch(mpStream->proto.ipProto) + // Check and populate L4 protocol + switch(mpStream->l4Proto()) { - case IP_PROTO_TCP: + case Stream::e_l4_none: + goto _data; + break; + case Stream::e_l4_tcp: proto = PTYP_L4_TCP; break; - case IP_PROTO_UDP: + case Stream::e_l4_udp: proto = PTYP_L4_UDP; break; - case IP_PROTO_ICMP: + case Stream::e_l4_icmp: proto = PTYP_L4_ICMP; break; - case IP_PROTO_IGMP: + case Stream::e_l4_igmp: proto = PTYP_L4_IGMP; break; default: - qDebug("%s: Unsupported ipProto", __FUNCTION__); + qDebug("%s: Unsupported L4 Proto %d", __FUNCTION__, + mpStream->l4Proto()); proto = PTYP_INVALID; }; mPacketProtocols.append(proto); _data: mPacketProtocols.append(PTYP_DATA); -#endif } - +/*! + Returns the count of protocols in the current stream +*/ int PacketModel::protoCount() const { return mPacketProtocols.count(); } +/*! + Returns the count of fields in the given protocol +*/ int PacketModel::fieldCount(int protocol) const { ProtocolInfo proto; @@ -375,25 +427,22 @@ QVariant PacketModel::ethField(int field, int role) const FieldInfo info; // FIXME(MED): Mac Addr formatting -#if 0 // FIXME protobuf switch(field) { case 0: info.name = QString("Destination Mac Address"); - info.textValue = QString("%1%2"). - arg(mpStream->l2.eth.dstMacMshw, 4, BASE_HEX, QChar('0')). - arg(mpStream->l2.eth.dstMacLsw, 8, BASE_HEX, QChar('0')); + info.textValue = QString("%1"). + arg(mpStream->mac()->dstMac(), 12, BASE_HEX, QChar('0')); break; case 1: info.name = QString("Source Mac Address"); - info.textValue = QString("%1%2"). - arg(mpStream->l2.eth.srcMacMshw, 4, BASE_HEX, QChar('0')). - arg(mpStream->l2.eth.srcMacLsw, 8, BASE_HEX, QChar('0')); + info.textValue = QString("%1"). + arg(mpStream->mac()->srcMac(), 12, BASE_HEX, QChar('0')); break; case 2: info.name = QString("Type"); info.textValue = QString("0x%1"). - arg(mpStream->proto.etherType, 4, BASE_HEX, QChar('0')); + arg(mpStream->eth2()->type(), 4, BASE_HEX, QChar('0')); break; default: info.name = QString(); @@ -411,7 +460,6 @@ QVariant PacketModel::ethField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -419,23 +467,22 @@ QVariant PacketModel::llcField(int field, int role) const { FieldInfo info; -#if 0 // FIXME: protobuf switch(field) { case 0: info.name = QString("DSAP"); info.textValue = QString("0x%1"). - arg(mpStream->proto.dsap, 2, BASE_HEX, QChar('0')); + arg(mpStream->llc()->dsap(), 2, BASE_HEX, QChar('0')); break; case 1: info.name = QString("SSAP"); info.textValue = QString("0x%1"). - arg(mpStream->proto.ssap, 2, BASE_HEX, QChar('0')); + arg(mpStream->llc()->ssap(), 2, BASE_HEX, QChar('0')); break; case 2: info.name = QString("Control"); info.textValue = QString("0x%1"). - arg(mpStream->proto.ctl, 2, BASE_HEX, QChar('0')); + arg(mpStream->llc()->ctl(), 2, BASE_HEX, QChar('0')); break; default: info.name = QString(); @@ -453,7 +500,6 @@ QVariant PacketModel::llcField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -461,19 +507,17 @@ QVariant PacketModel::snapField(int field, int role) const { FieldInfo info; -#if 0 // FIXME: protobuf switch(field) { case 0: info.name = QString("OUI"); - info.textValue = QString("0x%1%2"). - arg(mpStream->proto.ouiMsb, 2, BASE_HEX, QChar('0')). - arg(mpStream->proto.ouiLshw, 4, BASE_HEX, QChar('0')); + info.textValue = QString("0x%1"). + arg(mpStream->snap()->oui(), 6, BASE_HEX, QChar('0')); break; case 1: info.name = QString("Type"); info.textValue = QString("0x%1"). - arg(mpStream->proto.etherType, 4, BASE_HEX, QChar('0')); + arg(mpStream->eth2()->type(), 4, BASE_HEX, QChar('0')); break; default: info.name = QString(); @@ -491,7 +535,6 @@ QVariant PacketModel::snapField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -499,28 +542,27 @@ QVariant PacketModel::svlanField(int field, int role) const { FieldInfo info; -#if 0 // FIXME: protobuf switch(field) { case 0: info.name = QString("TPID"); info.textValue = QString("0x%1"). - arg(mpStream->l2.eth.stpid, 4, BASE_HEX, QChar('0')); + arg(mpStream->vlan()->stpid(), 4, BASE_HEX, QChar('0')); break; case 1: info.name = QString("PCP"); info.textValue = QString("%1"). - arg(mpStream->l2.eth.svlanPrio); + arg(mpStream->vlan()->svlanPrio()); break; case 2: info.name = QString("DE"); info.textValue = QString("%1"). - arg(mpStream->l2.eth.svlanCfi); + arg(mpStream->vlan()->svlanCfi()); break; case 3: info.name = QString("VlanId"); info.textValue = QString("%1"). - arg(mpStream->l2.eth.svlanId); + arg(mpStream->vlan()->svlanId()); break; default: info.name = QString(); @@ -538,7 +580,6 @@ QVariant PacketModel::svlanField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -547,66 +588,65 @@ QVariant PacketModel::ipField(int field, int role) const { FieldInfo info; -#if 0 // FIXME: protobuf switch(field) { case 0: info.name = QString("Version"); info.textValue = QString("%1"). - arg(mpStream->l3.ip.ver); + arg(mpStream->ip()->ver()); break; case 1: info.name = QString("Header Length"); info.textValue = QString("%1"). - arg(mpStream->l3.ip.hdrLen); + arg(mpStream->ip()->hdrLen()); break; case 2: info.name = QString("TOS/DSCP"); info.textValue = QString("0x%1"). - arg(mpStream->l3.ip.tos, 2, BASE_HEX, QChar('0')); + arg(mpStream->ip()->tos(), 2, BASE_HEX, QChar('0')); break; case 3: info.name = QString("Total Length"); info.textValue = QString("%1"). - arg(mpStream->l3.ip.totLen); + arg(mpStream->ip()->totLen()); break; case 4: info.name = QString("ID"); info.textValue = QString("0x%1"). - arg(mpStream->l3.ip.id, 2, BASE_HEX, QChar('0')); + arg(mpStream->ip()->id(), 2, BASE_HEX, QChar('0')); break; case 5: info.name = QString("Flags"); info.textValue = QString("0x%1"). - arg(mpStream->l3.ip.flags, 2, BASE_HEX, QChar('0')); // FIXME(HIGH) + arg(mpStream->ip()->flags(), 2, BASE_HEX, QChar('0')); // FIXME(HIGH) break; case 6: info.name = QString("Fragment Offset"); info.textValue = QString("%1"). - arg(mpStream->l3.ip.fragOfs); + arg(mpStream->ip()->fragOfs()); break; case 7: info.name = QString("TTL"); info.textValue = QString("%1"). - arg(mpStream->l3.ip.ttl); + arg(mpStream->ip()->ttl()); break; case 8: info.name = QString("Protocol Type"); info.textValue = QString("0x%1"). - arg(mpStream->l3.ip.proto, 2, BASE_HEX, QChar('0')); + arg(mpStream->ip()->proto(), 2, BASE_HEX, QChar('0')); break; case 9: info.name = QString("Checksum"); info.textValue = QString("0x%1"). - arg(mpStream->l3.ip.cksum, 4, BASE_HEX, QChar('0')); + arg(mpStream->ip()->cksum(), 4, BASE_HEX, QChar('0')); break; case 10: info.name = QString("Source IP"); - info.textValue = QHostAddress(mpStream->l3.ip.srcIp).toString(); + info.textValue = QHostAddress(mpStream->ip()->srcIp()).toString(); break; case 11: info.name = QString("Destination IP"); - info.textValue = QHostAddress(mpStream->l3.ip.dstIp).toString(); + info.textValue = QHostAddress(mpStream->ip()->dstIp()).toString(); break; default: info.name = QString(); @@ -624,7 +664,6 @@ QVariant PacketModel::ipField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -633,58 +672,57 @@ QVariant PacketModel::tcpField(int field, int role) const { FieldInfo info; -#if 0 // FIXME: protobuf switch(field) { case 0: info.name = QString("Source Port"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.srcPort); + arg(mpStream->tcp()->srcPort()); break; case 1: info.name = QString("Destination Port"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.dstPort); + arg(mpStream->tcp()->dstPort()); break; case 2: info.name = QString("Seq Number"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.seqNum); + arg(mpStream->tcp()->seqNum()); break; case 3: info.name = QString("Ack Number"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.ackNum); + arg(mpStream->tcp()->ackNum()); break; case 4: info.name = QString("Header Length"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.hdrLen); + arg(mpStream->tcp()->hdrLen()); break; case 5: info.name = QString("Reserved"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.rsvd); + arg(mpStream->tcp()->rsvd()); break; case 6: info.name = QString("Flags"); info.textValue = QString("0x%1"). - arg(mpStream->l4.tcp.flags, 2, BASE_HEX, QChar('0')); + arg(mpStream->tcp()->flags(), 2, BASE_HEX, QChar('0')); break; case 7: info.name = QString("Window"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.flags); + arg(mpStream->tcp()->flags()); break; case 8: info.name = QString("Checksum"); info.textValue = QString("0x%1"). - arg(mpStream->l4.tcp.cksum, 4, BASE_HEX, QChar('0')); + arg(mpStream->tcp()->cksum(), 4, BASE_HEX, QChar('0')); break; case 9: info.name = QString("Urgent Pointer"); info.textValue = QString("%1"). - arg(mpStream->l4.tcp.urgPtr); + arg(mpStream->tcp()->urgPtr()); break; default: info.name = QString(); @@ -702,7 +740,6 @@ QVariant PacketModel::tcpField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -711,28 +748,27 @@ QVariant PacketModel::udpField(int field, int role) const { FieldInfo info; -#if 0 // FIXME:protobuf switch(field) { case 0: info.name = QString("Source Port"); info.textValue = QString("%1"). - arg(mpStream->l4.udp.srcPort); + arg(mpStream->udp()->srcPort()); break; case 1: info.name = QString("Destination Port"); info.textValue = QString("%1"). - arg(mpStream->l4.udp.dstPort); + arg(mpStream->udp()->dstPort()); break; case 2: info.name = QString("Total Length"); info.textValue = QString("%1"). - arg(mpStream->l4.udp.totLen); + arg(mpStream->udp()->totLen()); break; case 3: info.name = QString("Checksum"); info.textValue = QString("0x%1"). - arg(mpStream->l4.udp.cksum, 4, BASE_HEX, QChar('0')); + arg(mpStream->udp()->cksum(), 4, BASE_HEX, QChar('0')); break; default: info.name = QString(); @@ -750,7 +786,6 @@ QVariant PacketModel::udpField(int field, int role) const } Q_ASSERT(1 == 1); // Unreachable code -#endif return QVariant(); } @@ -900,3 +935,4 @@ void PacketModel::registerData() registerProto(PTYP_DATA, "Data", "data"); } +#endif diff --git a/client/packetmodel.h b/client/packetmodel.h index 4f79155..1134a14 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -4,6 +4,8 @@ #include #include "stream.h" +#define NEW_IMPL // FIXME(HI) - Use this and remove old one + class PacketModel: public QAbstractItemModel { @@ -27,11 +29,15 @@ private: quint16 type; #define ITYP_PROTOCOL 1 #define ITYP_FIELD 2 - quint16 protocol; + quint16 protocol; // protocol is valid for both ITYPs } ws; } IndexId; Stream *mpStream; + +#ifdef NEW_IMPL +// Nothing - required stuff is part of Stream Class +#else QList mPacketProtocols; typedef struct @@ -49,6 +55,8 @@ private: QList fieldList; } ProtocolInfo; + //! Contains registration info (name, size etc) for all protocols + // and fields within the protocol QList mProtocols; void registerProto(uint handle, char *name, char *abbr); void registerField(uint protoHandle, char *name, char *abbr); @@ -102,6 +110,8 @@ private: #define PTYP_INVALID 0 #define PTYP_DATA 0xFF +#endif // NEW_IMPL + #define FROL_NAME 1 #define FROL_TEXT_VALUE 2 diff --git a/client/port.cpp b/client/port.cpp index 2196e7d..89d94c3 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -5,88 +5,177 @@ #include "port.h" #include "pbhelper.h" +uint Port::mAllocStreamId = 0; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} Port::Port(quint32 id, quint32 portGroupId) { - d.set_port_id(id); + mPortId = id; + d.mutable_port_id()->set_id(id); mPortGroupId = portGroupId; - - -#if 0 // PB - // FIXME(HI): TEST only - for(int i = 0; i < 10; i++) - mPortStats[i] = mPortGroupId*10000+mPortId*100+i; -#endif } -void Port::updatePortConfig(OstProto::PortConfig *portConfig) +void Port::updatePortConfig(OstProto::Port *port) { + d.MergeFrom(*port); +} - PbHelper pbh; +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i].setOrdinal(i); +} - pbh.update(&d, portConfig); -#if 0 - const ::google::protobuf::Message::Reflection *ref1; - ::google::protobuf::Message::Reflection *ref2; - std::vector list; +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams); +} - qDebug("In %s", __FUNCTION__); +bool Port::newStreamAt(int index) +{ + Stream s; - ref1 = portConfig.GetReflection(); - ref1->ListFields(&list); + if (index > mStreams.size()) + return false; - ref2 = d.GetReflection(); + s.setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); - for (uint i=0; i < list.size(); i++) + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + mStreams.removeAt(index); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream s; + + s.setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) { - const ::google::protobuf::FieldDescriptor *f1, *f2; + if (streamId == mStreams[i].id()) + goto _found; + } - f1 = list[i]; - f2 = d.GetDescriptor()->FindFieldByName(f1->name()); - switch(f2->type()) + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex].update(stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) { - case ::google::protobuf::FieldDescriptor::TYPE_UINT32: - ref2->SetUInt32(f2, ref1->GetUInt32(f1)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_BOOL: - ref2->SetBool(f2, ref1->GetBool(f1)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_STRING: - ref2->SetString(f2, ref1->GetString(f1)); - break; - default: - qDebug("unhandled Field Type"); - break; + if (mLastSyncStreamList[i] == mStreams[j].id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); } } - - if (msg->GetDescriptor() != OstProto::PortConfig::descriptor()) - { - qDebug("%s: invalid Message Descriptor (%s)", __FUNCTION__, - msg->GetDescriptor()->name()); - goto _error_exit; - } - - portConfig = msg; - - // check for "required" param - if (!portConfig.has_port_id()) - { - qDebug("%s: invalid Message Descriptor (%s)", __FUNCTION__, - msg->GetDescriptor()->name()); - goto _error_exit; - } -#endif } -void Port::insertDummyStreams() +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) { - 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 + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i].id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i].id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i].getConfig(mPortId, s); + } } +// +// ----------- SLOTS ------------- +// +void Port::when_syncComplete() +{ + qSort(mStreams); + + mLastSyncStreamList.clear(); + for (int i=0; i #include "stream.h" -class StreamModel; +//class StreamModel; class Port { -#if 0 // PB - friend class PortStatsModel; -#endif - friend class StreamModel; - - //friend class PbHelper; - - // FIXME: non-friend mechanism - //friend QList* StreamModel::currentPortStreamList(void); + //friend class StreamModel; private: - OstProto::PortConfig d; + static uint mAllocStreamId; + OstProto::Port d; + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' quint32 mPortId; quint32 mPortGroupId; QString mUserAlias; // user defined - QList mStreams; - -#if 0 // PB - quint32 mPortId; - QString mName; - QString mDescription; - AdminStatus mAdminStatus; - OperStatus mOperStatus; - ControlMode mControlMode; - - quint32 mPortStats[10]; // FIXME(HI):Hardcoding -#endif + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); public: enum AdminStatus { AdminDisable, AdminEnable }; enum OperStatus { OperDown, OperUp }; @@ -52,7 +39,7 @@ public: const QString& userAlias() const { return mUserAlias; } quint32 id() const - { return d.port_id(); } + { return d.port_id().id(); } const QString name() const { return QString().fromStdString(d.name()); } const QString description() const @@ -75,10 +62,34 @@ public: void setAlias(QString &alias) { mUserAlias = alias; } //void setExclusive(bool flag); - void updatePortConfig(OstProto::PortConfig *portConfig); + int numStreams() { return mStreams.size(); } + Stream& streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } - // FIXME(HIGH): Only for testing - void insertDummyStreams(); + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); }; #endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp index fa5fa0e..3de90cc 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -10,29 +10,12 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) // Allocate an id for self mPortGroupId = PortGroup::mPortGroupAllocId++; -#if 0 // PB - // Init attributes for which we values were passed to us - mServerAddress = ip; - mServerPort = port; - - // Init remaining attributes with defaults - mpSocket = new QTcpSocket(this); -#endif - rpcChannel = new PbRpcChannel(ip, port); rpcController = new PbRpcController(); serviceStub = new OstProto::OstService::Stub(rpcChannel, OstProto::OstService::STUB_OWNS_CHANNEL); -#if 0 // PB - // TODO: consider using QT's signal-slot autoconnect - connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_mpSocket_stateChanged())); - connect(mpSocket, SIGNAL(connected()), this, SLOT(when_connected())); - connect(mpSocket, SIGNAL(disconnected()), this, SLOT(when_disconnected())); - connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(when_error(QAbstractSocket::SocketError))); - connect(mpSocket, SIGNAL(readyRead()), this, SLOT(when_dataAvail())); -#endif - // FIXME:Can't for my life figure out why this ain't working! + // FIXME(LOW):Can't for my life figure out why this ain't working! //QMetaObject::connectSlotsByName(this); connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_rpcChannel_stateChanged())); @@ -52,92 +35,6 @@ PortGroup::~PortGroup() delete serviceStub; } -#if 0 // PB -void PortGroup::connectToHost(QHostAddress ip, quint16 port) -{ - rpcChannel->establish(ip, port) -} - -void PortGroup::connectToHost() -{ - qDebug("PortGroup::connectToHost()"); - rpcChannel->establish() -} - -void PortGroup::disconnectFromHost() -{ - mpSocket->disconnectFromHost(); -} -#endif - -#if 0 // PB -// -------------------------------------------- -// Private Methods -// -------------------------------------------- -void PortGroup::ProcessMsg(const char *msg, quint32 size) -{ - tCommHdr *hdr; - // TODO: For now, assuming we'll get a complete msg - // but need to fix this as this is a TCP stream - - hdr = (tCommHdr*) msg; - - if (hdr->ver != 1) // FIXME:hardcoding - { - qDebug("Rcvd msg with invalid version %d\n", hdr->ver); - goto _exit; - } - - qDebug("msgType - %x\n", NTOHS(hdr->msgType)); - switch (NTOHS(hdr->msgType)) - { - case e_MT_CapabilityInfo: - ProcessCapabilityInfo(msg+sizeof(tCommHdr), - NTOHS(hdr->msgLen)-sizeof(tCommHdr)); - break; - - default: - qDebug("Rcvd msg with unrecognized msgType %d\n", NTOHS(hdr->msgType)); - } - -_exit: - return; -} - -void PortGroup::ProcessCapabilityInfo(const char *msg, qint32 size) -{ - tTlvPortCapability *cap = (tTlvPortCapability*) msg; - Port *p; - - emit portListAboutToBeChanged(mPortGroupId); - - while (size) - { - qDebug("size = %d, tlvType = %d, tlvLen = %d\n", - size, NTOHS(cap->tlvType), NTOHS(cap->tlvLen)); - if (NTOHS(cap->tlvType) != e_TT_PortCapability) - { - qDebug("Unrecognized TLV Type %d\n", NTOHS(cap->tlvType)); - goto _next; - } - - 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); - -_next: - size -= NTOHS(cap->tlvLen); - cap = (tTlvPortCapability*)((char *)(cap) + NTOHS(cap->tlvLen)); - } - - emit portListChanged(mPortGroupId); - - return; -} -#endif // ------------------------------------------------ // Slots @@ -161,18 +58,6 @@ void PortGroup::on_rpcChannel_connected() rpcController->Reset(); serviceStub->getPortIdList(rpcController, &void_, portIdList, NewCallback(this, &PortGroup::processPortIdList, portIdList)); - -#if 0 // PB - // Ask for Port Capability - tCommHdr pkt; - pkt.ver = 1; - pkt.resv1 = 0; - pkt.resv2 = 0; - pkt.msgType = HTONS(e_MT_GetCapability); - pkt.msgLen = HTONS(8); - - mpSocket->write((char*) &pkt, sizeof(pkt)); -#endif } void PortGroup::on_rpcChannel_disconnected() @@ -190,20 +75,94 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) emit portGroupDataChanged(this); } -#if 0 // PB -void PortGroup::when_dataAvail() +void PortGroup::when_configApply(int portIndex, uint *cookie) { - qDebug("dataAvail\n"); - - QByteArray msg = mpSocket->read(1024); // FIXME: hardcoding - ProcessMsg(msg.constData(), msg.size()); + uint *op; + OstProto::Ack *ack; + + Q_ASSERT(portIndex < mPorts.size()); + + if (cookie == NULL) + { + // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] + // cookie[1]: *ack + cookie = new uint[2]; + ack = new OstProto::Ack; + + cookie[0] = (uint) 0; + cookie[1] = (uint) ack; + } + else + { + ack = (OstProto::Ack*) cookie[1]; + } + + Q_ASSERT(cookie != NULL); + op = &cookie[0]; + + switch (*op) + { + case 0: + { + OstProto::StreamIdList streamIdList; + + qDebug("applying 'deleted streams' ..."); + + streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); + mPorts[portIndex].getDeletedStreamsSinceLastSync(streamIdList); + + (*op)++; + rpcController->Reset(); + serviceStub->deleteStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 1: + { + OstProto::StreamIdList streamIdList; + + qDebug("applying 'new streams' ..."); + + streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); + mPorts[portIndex].getNewStreamsSinceLastSync(streamIdList); + + (*op)++; + rpcController->Reset(); + serviceStub->addStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 2: + { + OstProto::StreamConfigList streamConfigList; + + qDebug("applying 'modified streams' ..."); + + streamConfigList.mutable_port_id()->set_id(mPorts[portIndex].id()); + mPorts[portIndex].getModifiedStreamsSinceLastSync(streamConfigList); + + (*op)++; + rpcController->Reset(); + serviceStub->modifyStream(rpcController, &streamConfigList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 3: + qDebug("apply completed"); + delete cookie; + break; + + default: + qDebug("%s: Unknown Op!!!", __FUNCTION__); + break; + } } -#endif void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) { - int count; - qDebug("got a portlist ..."); if (rpcController->Failed()) @@ -212,26 +171,21 @@ void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) goto _error_exit; } - count = portIdList->port_id_size(); - qDebug("%s: portid count = %d", __FUNCTION__, count); - qDebug("%s: %s", __FUNCTION__, portIdList->DebugString().c_str()); - emit portListAboutToBeChanged(mPortGroupId); - for(int i = 0; i < count; i++) + for(int i = 0; i < portIdList->port_id_size(); i++) { Port *p; - p = new Port(portIdList->port_id(i), mPortGroupId); - //p->setName("name"); - //p->setDescription("Desc"); - p->insertDummyStreams(); // FIXME: only for testing + p = new Port(portIdList->port_id(i).id(), mPortGroupId); qDebug("before port append\n"); mPorts.append(*p); } emit portListChanged(mPortGroupId); + this->portIdList.CopyFrom(*portIdList); + // Request PortConfigList { OstProto::PortConfigList *portConfigList; @@ -253,8 +207,6 @@ _exit: void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) { - int count; - qDebug("In %s", __FUNCTION__); if (rpcController->Failed()) @@ -263,19 +215,15 @@ void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) goto _error_exit; } - count = portConfigList->list_size(); - qDebug("%s: count = %d", __FUNCTION__, count); - qDebug("%s: <%s>", __FUNCTION__, portConfigList->DebugString().c_str()); - emit portListAboutToBeChanged(mPortGroupId); - for(int i = 0; i < count; i++) + for(int i = 0; i < portConfigList->port_size(); i++) { uint id; - id = portConfigList->list(i).port_id(); + id = portConfigList->port(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] - mPorts[id].updatePortConfig(portConfigList->mutable_list(i)); + mPorts[id].updatePortConfig(portConfigList->mutable_port(i)); } emit portListChanged(mPortGroupId); @@ -283,6 +231,185 @@ void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) // FIXME: check if we need new signals since we are not changing the // number of ports, just the port data + if (numPorts() > 0) + getStreamIdList(); + _error_exit: delete portConfigList; } + +void PortGroup::getStreamIdList(int portIndex, + OstProto::StreamIdList *streamIdList) +{ + ::OstProto::PortId portId; + + qDebug("In %s", __FUNCTION__); + + if (streamIdList == NULL) + { + // First invocation (uses default params) - + // request StreamIdList for first port + + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); + streamIdList = new ::OstProto::StreamIdList(); + + goto _request; + } + + qDebug("got a streamIdlist ..."); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; // FIXME(MED): Partial RPC + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex].id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex].id(), + portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } + + // FIXME(MED): need to mPorts.clear()??? + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex].insertStream(streamId); + } + +_next_port: + // FIXME(HI): ideally we shd use signals/slots but this means + // we will have to use Port* instead of Port with QList<> - + // need to find a way for this + mPorts[portIndex].when_syncComplete(); + portIndex++; + if (portIndex >= numPorts()) + { + // We're done for all ports !!! + + // FIXME(HI): some way to reset streammodel + + delete streamIdList; + + if (numPorts() > 0) + getStreamConfigList(); + + goto _exit; + } + +_request: + portId.set_id(mPorts[portIndex].id()); + streamIdList->Clear(); + + rpcController->Reset(); + serviceStub->getStreamIdList(rpcController, &portId, streamIdList, + NewCallback(this, &PortGroup::getStreamIdList, + portIndex, streamIdList)); + + goto _exit; + + + +_exit: + return; +} + +void PortGroup::getStreamConfigList(int portIndex, + OstProto::StreamConfigList *streamConfigList) +{ + OstProto::StreamIdList streamIdList; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (streamConfigList == NULL) + { + // First invocation using default params + // - request for first port + + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); + + streamConfigList = new OstProto::StreamConfigList; + + goto _request; + } + + qDebug("got a streamconfiglist"); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex].id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamConfigList->port_id().id(), + mPorts[portIndex].id(), portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } + + // FIXME(MED): need to mStreams.clear()??? + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex].updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + +_next_port: + portIndex++; + + if (portIndex >= numPorts()) + { + // We're done for all ports !!! + + // FIXME(HI): some way to reset streammodel + + delete streamConfigList; + goto _exit; + } + +_request: + qDebug("requesting stream config list ..."); + + streamIdList.Clear(); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); + for (int j = 0; j < mPorts[portIndex].numStreams(); j++) + { + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mPorts[portIndex].streamByIndex(j).id()); + } + streamConfigList->Clear(); + + rpcController->Reset(); + serviceStub->getStreamConfig(rpcController, + &streamIdList, streamConfigList, NewCallback(this, + &PortGroup::getStreamConfigList, portIndex, streamConfigList)); + +_exit: + return; +} + +void PortGroup::processModifyStreamAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("Modify Successful!!"); + + // TODO(HI): Apply Button should now be disabled???!!!!??? +} diff --git a/client/portgroup.h b/client/portgroup.h index 528f48b..89c02af 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -31,7 +31,9 @@ private: #endif PbRpcChannel *rpcChannel; ::google::protobuf::RpcController *rpcController; - OstProto::OstService::Stub *serviceStub; + ::OstProto::OstService::Stub *serviceStub; + + ::OstProto::PortIdList portIdList; public: // FIXME(HIGH): member access QList mPorts; @@ -61,6 +63,12 @@ public: void processPortIdList(OstProto::PortIdList *portIdList); void processPortConfigList(OstProto::PortConfigList *portConfigList); + void getStreamIdList(int portIndex = 0, + OstProto::StreamIdList *streamIdList = NULL); + void getStreamConfigList(int portIndex = 0, + OstProto::StreamConfigList *streamConfigList = NULL); + + void processModifyStreamAck(OstProto::Ack *ack); signals: void portGroupDataChanged(PortGroup* portGroup); void portListAboutToBeChanged(quint32 portGroupId); @@ -71,6 +79,9 @@ private slots: void on_rpcChannel_connected(); void on_rpcChannel_disconnected(); void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + +public slots: + void when_configApply(int portIndex, uint *cookie = NULL); #if 0 // PB void on_rpcChannel_when_dataAvail(); #endif diff --git a/client/portgrouplist.h b/client/portgrouplist.h index 1e5add9..84bdb9c 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -31,6 +31,7 @@ public: PortModel* getPortModel() { return &mPortGroupListModel; } PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } StreamModel* getStreamModel() { return &mStreamListModel; } + bool isPortGroup(const QModelIndex& index); bool isPort(const QModelIndex& index); PortGroup& portGroup(const QModelIndex& index); diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 435344c..5da586b 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -73,7 +73,8 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const { #if 0 // PB return pgl->mPortGroups.at(pgidx)->mPorts.at(pidx).mPortStats[index.row()]; -#endif return 0; //FIXME: Get actual port stats +#endif + return 0; //FIXME: Get actual port stats } else return QVariant(); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 2e6df17..f711f55 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -56,9 +56,13 @@ void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) qDebug("%s: invalid index", __FUNCTION__); return; } +#if 0 // CleanedUp! // FIXME(MED): This way of passing params must be changed scd = new StreamConfigDialog(plm->getStreamModel()->currentPortStreamList(), (uint) index.row(), this); +#endif + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); qDebug("stream list activated\n"); scd->exec(); // TODO: chk retval delete scd; @@ -206,7 +210,43 @@ _EXIT: void PortsWindow::on_pbApply_clicked() { -{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 // TODO (LOW): This block is for testing only QModelIndex current = tvPortList->selectionModel()->currentIndex(); @@ -214,7 +254,7 @@ void PortsWindow::on_pbApply_clicked() qDebug("current = %llx", current.internalId()); else qDebug("current is invalid"); -} +#endif } void PortsWindow::on_actionNew_Port_Group_triggered() diff --git a/client/stream.cpp b/client/stream.cpp index e153b8e..30568e5 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -1,134 +1,851 @@ -#include +#include + +#include "stream.h" + +#include + +#define BASE_HEX 16 + +QString MacProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Destination Mac Address"); + break; + case 1: + name = QString("Source Mac Address"); + break; + default: + name = QString(); + break; + } + + return name; +} + +QString MacProtocol::fieldTextValue(int index) +{ + QString textValue; + + // FIXME(MED): Mac Addr formatting + switch(index) + { + case 0: + textValue = QString("%1"). + arg(dstMac(), 12, BASE_HEX, QChar('0')); + break; + case 1: + textValue = QString("%1"). + arg(srcMac(), 12, BASE_HEX, QChar('0')); + break; + default: + textValue = QString(); + break; + } + + return textValue; +} + +QByteArray MacProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString LlcProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("DSAP"); + break; + case 1: + name = QString("SSAP"); + break; + case 2: + name = QString("Control"); + break; + default: + name = QString(); + break; + } + + return name; +} + +QString LlcProtocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("0x%1"). + arg(dsap(), 2, BASE_HEX, QChar('0')); + break; + case 1: + textValue = QString("0x%1"). + arg(ssap(), 2, BASE_HEX, QChar('0')); + break; + case 2: + textValue = QString("0x%1"). + arg(ctl(), 2, BASE_HEX, QChar('0')); + break; + default: + textValue = QString(); + break; + } + + return textValue; +} + +QByteArray LlcProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString SnapProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("OUI"); + break; + default: + name = QString(); + break; + } + + return name; +} + +QString SnapProtocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("0x%1"). + arg(oui(), 6, BASE_HEX, QChar('0')); + break; + default: + textValue = QString(); + break; + } + + return textValue; +} + +QByteArray SnapProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString Eth2Protocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Type"); + break; + default: + name = QString(); + break; + } + return name; +} + +QString Eth2Protocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("0x%1"). + arg(type(), 4, BASE_HEX, QChar('0')); + break; + default: + textValue = QString(); + break; + } + return textValue; +} + +QByteArray Eth2Protocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +int VlanProtocol::numFields() +{ + if (isSingleTagged()) + return 4; + else if (isDoubleTagged()) + return 8; + else + { + Q_ASSERT(isUntagged()); + return 0; + } +} + +QString VlanProtocol::fieldName(int index) +{ + QString name; + + if (isDoubleTagged()) + { + switch(index) + { + case 0: + name = QString("TPID"); + break; + case 1: + name = QString("PCP"); + break; + case 2: + name = QString("DE"); + break; + case 3: + name = QString("VlanId"); + break; + default: + index -= 4; + goto _single_tag; + } + + goto _exit; + } + +_single_tag: + switch(index) + { + case 0: + name = QString("TPID"); + break; + case 1: + name = QString("Priority"); + break; + case 2: + name = QString("CFI"); + break; + case 3: + name = QString("VlanId"); + break; + default: + name = QString(); + break; + } + +_exit: + return name; +} + +QString VlanProtocol::fieldTextValue(int index) +{ + QString textValue; + + if (isDoubleTagged()) + { + switch(index) + { + case 0: + textValue = QString("0x%1"). + arg(stpid(), 4, BASE_HEX, QChar('0')); + break; + case 1: + textValue = QString("%1"). + arg(svlanPrio()); + break; + case 2: + textValue = QString("%1"). + arg(svlanCfi()); + break; + case 3: + textValue = QString("%1"). + arg(svlanId()); + break; + default: + index -= 4; + goto _single_tag; + } + + goto _exit; + } + +_single_tag: + switch(index) + { + case 0: + textValue = QString("0x%1"). + arg(ctpid(), 4, BASE_HEX, QChar('0')); + break; + case 1: + textValue = QString("%1"). + arg(cvlanPrio()); + break; + case 2: + textValue = QString("%1"). + arg(cvlanCfi()); + break; + case 3: + textValue = QString("%1"). + arg(cvlanId()); + break; + default: + textValue = QString(); + break; + } + +_exit: + return textValue; +} + +QByteArray VlanProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString IpProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Version"); + break; + case 1: + name = QString("Header Length"); + break; + case 2: + name = QString("TOS/DSCP"); + break; + case 3: + name = QString("Total Length"); + break; + case 4: + name = QString("ID"); + break; + case 5: + name = QString("Flags"); + break; + case 6: + name = QString("Fragment Offset"); + break; + case 7: + name = QString("TTL"); + break; + case 8: + name = QString("Protocol Type"); + break; + case 9: + name = QString("Checksum"); + break; + case 10: + name = QString("Source IP"); + break; + case 11: + name = QString("Destination IP"); + break; + default: + name = QString(); + } + + return name; +} + +QString IpProtocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("%1"). + arg(ver()); + break; + case 1: + textValue = QString("%1"). + arg(hdrLen()); + break; + case 2: + textValue = QString("0x%1"). + arg(tos(), 2, BASE_HEX, QChar('0')); + break; + case 3: + textValue = QString("%1"). + arg(totLen()); + break; + case 4: + textValue = QString("0x%1"). + arg(id(), 2, BASE_HEX, QChar('0')); + break; + case 5: + textValue = QString("0x%1"). + arg(flags(), 2, BASE_HEX, QChar('0')); // FIXME(MED): bitmap? + break; + case 6: + textValue = QString("%1"). + arg(fragOfs()); + break; + case 7: + textValue = QString("%1"). + arg(ttl()); + break; + case 8: + textValue = QString("0x%1"). + arg(proto(), 2, BASE_HEX, QChar('0')); + break; + case 9: + textValue = QString("0x%1"). + arg(cksum(), 4, BASE_HEX, QChar('0')); + break; + case 10: + textValue = QHostAddress(srcIp()).toString(); + break; + case 11: + textValue = QHostAddress(dstIp()).toString(); + break; + default: + textValue = QString(); + } + + return textValue; +} + +QByteArray IpProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString TcpProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Source Port"); + break; + case 1: + name = QString("Destination Port"); + break; + case 2: + name = QString("Seq Number"); + break; + case 3: + name = QString("Ack Number"); + break; + case 4: + name = QString("Header Length"); + break; + case 5: + name = QString("Reserved"); + break; + case 6: + name = QString("Flags"); + break; + case 7: + name = QString("Window"); + break; + case 8: + name = QString("Checksum"); + break; + case 9: + name = QString("Urgent Pointer"); + break; + default: + name = QString(); + } + + return name; +} + +QString TcpProtocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("%1"). + arg(srcPort()); + break; + case 1: + textValue = QString("%1"). + arg(dstPort()); + break; + case 2: + textValue = QString("%1"). + arg(seqNum()); + break; + case 3: + textValue = QString("%1"). + arg(ackNum()); + break; + case 4: + textValue = QString("%1"). + arg(hdrLen()); + break; + case 5: + textValue = QString("%1"). + arg(rsvd()); + break; + case 6: + textValue = QString("0x%1"). + arg(flags(), 2, BASE_HEX, QChar('0')); + break; + case 7: + textValue = QString("%1"). + arg(flags()); + break; + case 8: + textValue = QString("0x%1"). + arg(cksum(), 4, BASE_HEX, QChar('0')); + break; + case 9: + textValue = QString("%1"). + arg(urgPtr()); + break; + default: + textValue = QString(); + } + + return textValue; +} + +QByteArray TcpProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + +QString UdpProtocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Source Port"); + break; + case 1: + name = QString("Destination Port"); + break; + case 2: + name = QString("Total Length"); + break; + case 3: + name = QString("Checksum"); + break; + default: + name = QString(); + } + + return name; +} + +QString UdpProtocol::fieldTextValue(int index) +{ + QString textValue; + + switch(index) + { + case 0: + textValue = QString("%1"). + arg(srcPort()); + break; + case 1: + textValue = QString("%1"). + arg(dstPort()); + break; + case 2: + textValue = QString("%1"). + arg(totLen()); + break; + case 3: + textValue = QString("0x%1"). + arg(cksum(), 4, BASE_HEX, QChar('0')); + break; + default: + textValue = QString(); + } + + return textValue; +} + +QByteArray UdpProtocol::fieldRawValue(int index) +{ + QByteArray rawValue; + + return rawValue; +} + + + +//----------------------------------------------------- +// Stream Class Methods +//----------------------------------------------------- -quint32 Stream::mAllocId = 0; Stream::Stream() { - mId = mAllocId++; - + mId = 0xFFFFFFFF; + mCore = new OstProto::StreamCore; + +// mCore->set_port_id(0xFFFFFFFF); +// mCore->set_stream_id(mId); + + mUnknown = new UnknownProtocol; + mMac = new MacProtocol; + + mLlc = new LlcProtocol; + mSnap = new SnapProtocol; + mEth2 = new Eth2Protocol; + mVlan = new VlanProtocol; + mIp = new IpProtocol; -#if 0 - // Default constructor - InitDefaultMeta(); - InitDefaultProto(); - InitDefaultL2(); - InitDefaultL3(); - InitDefaultL4(); -#endif + mArp = new ArpProtocol; + + mTcp = new TcpProtocol; + mUdp = new UdpProtocol; + mIcmp = new IcmpProtocol; + mIgmp = new IgmpProtocol; +} + +void Stream::getConfig(uint portId, OstProto::Stream *s) +{ + s->mutable_stream_id()->set_id(mId); + + s->mutable_core()->CopyFrom(*mCore); + + mMac->getConfig(s->mutable_mac()); + mMac->getConfig(s->mutable_mac()); + mLlc->getConfig(s->mutable_llc()); + mSnap->getConfig(s->mutable_snap()); + mEth2->getConfig(s->mutable_eth2()); + mVlan->getConfig(s->mutable_vlan()); + + mIp->getConfig(s->mutable_ip()); + mArp->getConfig(s->mutable_arp()); + + mTcp->getConfig(s->mutable_tcp()); + mUdp->getConfig(s->mutable_udp()); + mIcmp->getConfig(s->mutable_icmp()); + mIgmp->getConfig(s->mutable_igmp()); +} + +// FIXME(HIGH): Replace this by some Protocol Registration mechanism at Init +#define PTYP_L2_NONE 1 +#define PTYP_L2_ETH_2 2 +#define PTYP_L2_802_3_RAW 3 + +#define PTYP_L2_802_3_LLC 4 +#define PTYP_L2_SNAP 5 + +#define PTYP_VLAN 10 + +#define PTYP_L3_IP 30 +#define PTYP_L3_ARP 31 + +#define PTYP_L4_TCP 40 +#define PTYP_L4_UDP 41 +#define PTYP_L4_ICMP 42 +#define PTYP_L4_IGMP 43 + +#define PTYP_INVALID 0 +#define PTYP_DATA 0xFF + +void Stream::updateSelectedProtocols() +{ + int proto; + + // Clear the selected protocols list + selectedProtocols.clear(); + + // Check and populate L2 Protocol + switch(frameType()) + { + case Stream::e_ft_none: + proto = PTYP_L2_NONE; + break; + + case Stream::e_ft_eth_2: + selectedProtocols.append(PTYP_L2_NONE); + proto = PTYP_L2_ETH_2; + break; + + case Stream::e_ft_802_3_raw: + selectedProtocols.append(PTYP_L2_NONE); + proto = PTYP_L2_802_3_RAW; + break; + + case Stream::e_ft_802_3_llc: + selectedProtocols.append(PTYP_L2_NONE); + proto = PTYP_L2_802_3_LLC; + break; + + case Stream::e_ft_snap: + selectedProtocols.append(PTYP_L2_NONE); + selectedProtocols.append(PTYP_L2_802_3_LLC); + proto = PTYP_L2_SNAP; + break; + + default: + qDebug("%s: Unsupported frametype %d", __FUNCTION__, + frameType()); + proto = PTYP_INVALID; + } + selectedProtocols.append(proto); + + // Check and populate VLANs, if present + if (!vlan()->isUntagged()) + selectedProtocols.append(PTYP_VLAN); + + // Check and populate L3 protocols + switch (l3Proto()) + { + case Stream::e_l3_none : + goto _data; + break; + + case Stream::e_l3_ip : + proto = PTYP_L3_IP; + break; + + case Stream::e_l3_arp: + proto = PTYP_L3_ARP; + break; + + default: + qDebug("%s: Unsupported L3 Proto %d", __FUNCTION__, + l3Proto()); + proto = PTYP_INVALID; + } + selectedProtocols.append(proto); + + // Check and populate L4 protocol + switch(l4Proto()) + { + case Stream::e_l4_none: + goto _data; + break; + case Stream::e_l4_tcp: + proto = PTYP_L4_TCP; + break; + case Stream::e_l4_udp: + proto = PTYP_L4_UDP; + break; + case Stream::e_l4_icmp: + proto = PTYP_L4_ICMP; + break; + case Stream::e_l4_igmp: + proto = PTYP_L4_IGMP; + break; + default: + qDebug("%s: Unsupported L4 Proto %d", __FUNCTION__, + l4Proto()); + proto = PTYP_INVALID; + }; + selectedProtocols.append(proto); + +_data: + selectedProtocols.append(PTYP_DATA); +} + +int Stream::numProtocols() +{ + updateSelectedProtocols(); // FIXME(HI): shd not happen everytime + return selectedProtocols.size(); } #if 0 -void Stream::InitDefaultMeta() +int Stream::protocolId(int index) { - // TODO(LOW): Use #defines - meta.patternMode = e_dp_fixed; - meta.pattern = 0x00000000; - meta.dataStartOfs = 0; // FIXME(HIGH): this has to calculated - meta.lenMode = e_fl_fixed; - meta.frameLen = 64; - meta.frameLenMin = 64; - meta.frameLenMax = 1518; + updateSelectedProtocols(); // FIXME(HI): shd not happen everytime + if (index < selectedProtocols.size()) + return selectedProtocols.at(index); + else + return -1; } - -void Stream::InitDefaultProto() +int Stream::protocolIndex(int id) { - // TODO(LOW): Use #defines - proto.ft = e_ft_eth_2; - proto.dsap = 0x00; - proto.ssap = 0x00; - proto.ctl = 0x00; - proto.ouiMsb = 0x00; - proto.ouiLshw = 0x0000; - proto.protoMask = PM_L3_PROTO_NONE | PM_L4_PROTO_NONE; - proto.etherType = ETH_TYP_IP; - proto.ipProto = IP_PROTO_TCP; -} - - -void Stream::InitDefaultL2() -{ - // TODO(LOW): Use #defines - l2.eth.dstMacMshw = 0x0000; - l2.eth.dstMacLsw = 0x00000001; - l2.eth.dstMacMode = e_mm_fixed; - l2.eth.dstMacCount = 16; - l2.eth.dstMacStep = 1; - - l2.eth.srcMacMshw = 0x0000; - l2.eth.srcMacLsw = 0x00000002; - l2.eth.srcMacMode = e_mm_fixed; - l2.eth.srcMacCount = 16; - l2.eth.srcMacStep = 1; - - l2.eth.vlanMask = VM_UNTAGGED; - l2.eth.ctpid = 0x8100; - l2.eth.cvlanPrio = 0; - l2.eth.cvlanCfi = 0; - l2.eth.cvlanId = 2; - l2.eth.stpid = 0x88a8; - l2.eth.svlanPrio = 0; - l2.eth.svlanCfi = 0; - l2.eth.svlanId = 2; -} - -void Stream::InitDefaultL3() -{ - InitDefaultL3Ip(); -} - -void Stream::InitDefaultL3Ip() -{ - l3.ip.ipMask = STREAM_DEF_IP_MASK; - l3.ip.ver = STREAM_DEF_L3_IP_VER; - l3.ip.hdrLen = STREAM_DEF_L3_IP_HDR_LEN; - l3.ip.tos = STREAM_DEF_L3_IP_TOS; - l3.ip.totLen = STREAM_DEF_L3_IP_TOT_LEN; - l3.ip.id = STREAM_DEF_L3_IP_ID; - l3.ip.flags = STREAM_DEF_L3_IP_FLAGS; - l3.ip.fragOfs = STREAM_DEF_L3_IP_FRAG_OFS; - l3.ip.ttl = STREAM_DEF_L3_IP_TTL; - l3.ip.proto = STREAM_DEF_L3_IP_PROTO; - l3.ip.cksum = STREAM_DEF_L3_IP_CKSUM; - l3.ip.srcIp = STREAM_DEF_L3_IP_SRC_IP; - l3.ip.srcIpMode = STREAM_DEF_L3_IP_SRC_IP_MODE; - l3.ip.srcIpCount = STREAM_DEF_L3_IP_SRC_IP_COUNT; - l3.ip.srcIpMask = STREAM_DEF_L3_IP_SRC_IP_MASK; - l3.ip.dstIp = STREAM_DEF_L3_IP_DST_IP; - l3.ip.dstIpMode = STREAM_DEF_L3_IP_DST_IP_MODE; - l3.ip.dstIpCount = STREAM_DEF_L3_IP_DST_IP_COUNT; - l3.ip.dstIpMask = STREAM_DEF_L3_IP_DST_IP_MASK; -} - -void Stream::InitDefaultL4() -{ - InitDefaultL4Tcp(); - InitDefaultL4Udp(); -} - -void Stream::InitDefaultL4Tcp() -{ - l4.tcp.tcpMask = STREAM_DEF_L4_TCP_TCP_MASK; - l4.tcp.srcPort = STREAM_DEF_L4_TCP_SRC_PORT; - l4.tcp.dstPort = STREAM_DEF_L4_TCP_DST_PORT; - l4.tcp.seqNum = STREAM_DEF_L4_TCP_SEQ_NUM; - l4.tcp.ackNum = STREAM_DEF_L4_TCP_ACK_NUM; - l4.tcp.hdrLen = STREAM_DEF_L4_TCP_HDR_LEN; - l4.tcp.rsvd = STREAM_DEF_L4_TCP_RSVD; - l4.tcp.flags = STREAM_DEF_L4_TCP_FLAGS; - l4.tcp.window = STREAM_DEF_L4_TCP_WINDOW; - l4.tcp.cksum = STREAM_DEF_L4_TCP_CKSUM; - l4.tcp.urgPtr = STREAM_DEF_L4_TCP_URG_PTR; -} - -void Stream::InitDefaultL4Udp() -{ - l4.udp.udpMask = STREAM_DEF_L4_UDP_UDP_MASK; - l4.udp.srcPort = STREAM_DEF_L4_UDP_SRC_PORT; - l4.udp.dstPort = STREAM_DEF_L4_UDP_DST_PORT; - l4.udp.totLen = STREAM_DEF_L4_UDP_TOT_LEN; - l4.udp.cksum = STREAM_DEF_L4_UDP_CKSUM; } #endif + +AbstractProtocol* Stream::protocol(int index) +{ + int id; + + updateSelectedProtocols(); // FIXME(HI): shd not happen everytime + + id = selectedProtocols.at(index); + + switch(id) + { + case PTYP_L2_NONE: + return mac(); + case PTYP_L2_ETH_2: + return eth2(); + case PTYP_L2_802_3_RAW: + return eth2(); // FIXME(HI): define a dot3 protocol? + + case PTYP_L2_802_3_LLC: + return llc(); + + case PTYP_L2_SNAP: + return snap(); + + case PTYP_VLAN: + return vlan(); + + case PTYP_L3_IP: + return ip(); + case PTYP_L3_ARP: + return arp(); + + case PTYP_L4_TCP: + return tcp(); + case PTYP_L4_UDP: + return udp(); + case PTYP_L4_ICMP: + return icmp(); + case PTYP_L4_IGMP: + return igmp(); + + case PTYP_INVALID: + return mUnknown; + case PTYP_DATA: + return mUnknown; // FIXME(MED) define a "data" protocol? + default: + return mUnknown; + } +} + diff --git a/client/stream.h b/client/stream.h index 4b134cb..0bb35cd 100644 --- a/client/stream.h +++ b/client/stream.h @@ -3,6 +3,7 @@ #include #include +#include #include "../common/protocol.pb.h" class StreamConfigDialog; @@ -15,44 +16,55 @@ class PacketModel; #define IP_PROTO_TCP 0x06 #define IP_PROTO_UDP 0x11 -#if 0 - // Protocols - struct { - FrameType ft; - - - - quint16 protoMask; -#define PM_L3_PROTO_NONE 0x0001 -#define PM_L3_PROTO_OTHER 0x0002 -#define PM_L4_PROTO_NONE 0x0004 -#define PM_L4_PROTO_OTHER 0x0008 - - quint16 etherType; -#define ETH_TYP_IP 0x0800 -#define ETH_TYP_ARP 0x0806 - - quint16 ipProto; -#define IP_PROTO_ICMP 0x01 -#define IP_PROTO_IGMP 0x02 -#define IP_PROTO_TCP 0x06 -#define IP_PROTO_UDP 0x11 - } proto; - - // L2 - struct { - // Ethernet - - - - - } eth; - } l2; -#endif class AbstractProtocol { - // TODO + // TODO(LOW) +public: + /*! + Subclasses should return reference to their protocol specific + ::google::protobuf::Message + */ + virtual ::google::protobuf::Message& data() = 0; + /*! Subclasses can directly use this method. No need for overload */ + void getConfig(::google::protobuf::Message *msg) + { msg->CopyFrom(data()); } + + virtual QString protocolName() + { return QString("AbstractProtocol"); } + virtual QString protocolShortName() + { return QString("AbsProto"); } + virtual int numFields() + { return 1; } + virtual QString fieldName(int index) + { return QString("AbstractField"); } + virtual QString fieldTextValue(int index) + { return QString("AbstractFieldValue"); } + virtual QByteArray fieldRawValue(int index) + { return QByteArray(4, '\0'); } +}; + +class UnknownProtocol: public AbstractProtocol +{ + OstProto::Ack d; // FIXME(HI): replace 'Ack' with something else + +public: + virtual ~UnknownProtocol() {} + + virtual ::google::protobuf::Message& data() { return d; } + + virtual QString protocolName() + { return QString("UnknownProtocol"); } + QString protocolShortName() + { return QString("???Proto"); } + int numFields() + { return 1; } + QString fieldName(int index) + { return QString("UnknownField"); } + QString fieldTextValue(int index) + { return QString("UnknownFieldValue"); } + QByteArray fieldRawValue(int index) + { return QByteArray(4, '\0'); } }; class MacProtocol : public AbstractProtocol @@ -66,6 +78,10 @@ public: MacAddrInc, MacAddrDec }; + virtual ~MacProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + + bool update(OstProto::Mac mac) { d.MergeFrom(mac); return true; } // Dst Mac quint64 dstMac() @@ -110,6 +126,17 @@ public: { return d.src_mac_step(); } bool setSrcMacStep(quint16 srcMacStep) { d.set_src_mac_step(srcMacStep); return true; } + + + virtual QString protocolName() + { return QString("Media Access Control"); } + QString protocolShortName() + { return QString("MAC"); } + int numFields() + { return 2; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; class LlcProtocol : public AbstractProtocol @@ -118,6 +145,11 @@ private: OstProto::Llc d; public: + virtual ~LlcProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + + bool update(OstProto::Llc llc) { d.MergeFrom(llc); return true; } + quint8 dsap() { return d.dsap(); } bool setDsap(quint8 dsap) @@ -133,6 +165,16 @@ public: bool setCtl(quint8 ctl) { d.set_ctl(ctl); return true; } + + virtual QString protocolName() + { return QString("802.3 Logical Link Control"); } + QString protocolShortName() + { return QString("LLC"); } + int numFields() + { return 3; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; class SnapProtocol : public AbstractProtocol @@ -141,54 +183,179 @@ private: OstProto::Snap d; public: + virtual ~SnapProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Snap snap) { d.MergeFrom(snap); return true; } + quint32 oui() { return d.oui(); } bool setOui(quint32 oui) { d.set_oui(oui); return true; } +// "Type" field: use from eth2 +#if 0 quint16 type() { return d.type(); } bool setType(quint16 type) { d.set_type(type); return true; } +#endif + virtual QString protocolName() + { return QString("SubNetwork Access Protocol"); } + QString protocolShortName() + { return QString("SNAP"); } + int numFields() + { return 1; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; + class Eth2Protocol : public AbstractProtocol { private: OstProto::Eth2 d; public: + virtual ~Eth2Protocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Eth2 eth2) { d.MergeFrom(eth2); return true; } + quint16 type() { return d.type(); } bool setType(quint16 type) { d.set_type(type); return true; } + + + virtual QString protocolName() + { return QString("Protocol Type"); } + QString protocolShortName() + { return QString("TYPE"); } + int numFields() + { return 1; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; class VlanProtocol : public AbstractProtocol { -// TODO -#if 0 - quint16 vlanMask; -#define VM_UNTAGGED 0x0000 -#define VM_CVLAN_TAGGED 0x0001 -#define VM_CVLAN_TPID_OVERRIDE 0x0002 -#define VM_SVLAN_TAGGED 0x0100 -#define VM_SVLAN_TPID_OVERRIDE 0x0200 + OstProto::Vlan d; +public: + virtual ~VlanProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Vlan vlan) { d.MergeFrom(vlan); return true; } -#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)) + enum VlanFlag { + VlanCvlanTagged = 0x01, + VlanCtpidOverride = 0x02, + VlanSvlanTagged = 0x04, + VlanStpidOverride = 0x08, + }; + Q_DECLARE_FLAGS(VlanFlags, VlanFlag); - quint16 ctpid; - quint16 cvlanPrio : 3; - quint16 cvlanCfi : 1; - quint16 cvlanId : 13; - quint16 stpid; - quint16 svlanPrio : 3; - quint16 svlanCfi : 1; - quint16 svlanId : 13; -#endif + VlanFlags vlanFlags() + { + VlanFlags f; + + if (d.is_cvlan_tagged()) f|= VlanCvlanTagged; + if (d.is_ctpid_override()) f|= VlanCtpidOverride; + if (d.is_svlan_tagged()) f|= VlanSvlanTagged; + if (d.is_stpid_override()) f|= VlanStpidOverride; + + return f; + } + + bool setVlanFlags(VlanFlags vlanFlags) + { + d.set_is_cvlan_tagged(vlanFlags.testFlag(VlanCvlanTagged)); + d.set_is_ctpid_override(vlanFlags.testFlag(VlanCtpidOverride)); + d.set_is_svlan_tagged(vlanFlags.testFlag(VlanSvlanTagged)); + d.set_is_stpid_override(vlanFlags.testFlag(VlanStpidOverride)); + + return true; + } + + bool isUntagged() + { + if (!d.is_cvlan_tagged() && !d.is_svlan_tagged()) + return true; + else + return false; + } + + bool isSingleTagged() + { + if (( d.is_cvlan_tagged() && !d.is_svlan_tagged()) || + (!d.is_cvlan_tagged() && d.is_svlan_tagged()) ) + return true; + else + return false; + } + + bool isDoubleTagged() + { + if (d.is_cvlan_tagged() && d.is_svlan_tagged()) + return true; + else + return false; + } + + // CVLAN + quint16 ctpid() + { return d.ctpid(); } + bool setCtpid(quint16 ctpid) + { d.set_ctpid(ctpid); return true; } + + quint8 cvlanPrio() + { return (d.cvlan_tag() >> 13); } + bool setCvlanPrio(quint8 cvlanPrio) + { d.set_cvlan_tag((d.cvlan_tag() & 0x1FFF) | ((cvlanPrio & 0x3) << 13)); + return true; } + + quint8 cvlanCfi() + { return ((d.cvlan_tag() & 0x1000) >> 12); } + bool setCvlanCfi(quint8 cvlanCfi) + { d.set_cvlan_tag((d.cvlan_tag() & 0xEFFF) | ((cvlanCfi & 0x01) << 12)); + return true; } + + quint16 cvlanId() + { return (d.cvlan_tag() & 0x0FFF); } + bool setCvlanId(quint16 cvlanId) + { d.set_cvlan_tag((d.cvlan_tag() & 0xF000) | ((cvlanId & 0x0FFF))); + return true; } + + // SVLAN + quint16 stpid() + { return d.stpid(); } + bool setStpid(quint16 stpid) + { d.set_stpid(stpid); return true; } + quint8 svlanPrio() + { return (d.svlan_tag() >> 13); } + bool setSvlanPrio(quint8 svlanPrio) + { d.set_svlan_tag((d.svlan_tag() & 0x1FFF) | ((svlanPrio & 0x3) << 13)); + return true; } + + quint8 svlanCfi() + { return ((d.svlan_tag() & 0x1000) >> 12); } + bool setSvlanCfi(quint8 svlanCfi) + { d.set_svlan_tag((d.svlan_tag() & 0xEFFF) | ((svlanCfi & 0x01) << 12)); + return true; } + + quint16 svlanId() + { return (d.svlan_tag() & 0x0FFF); } + bool setSvlanId(quint16 svlanId) + { d.set_svlan_tag((d.svlan_tag() & 0xF000) | ((svlanId & 0x0FFF))); + return true; } + + virtual QString protocolName() + { return QString("Virtual Local Access Network"); } + QString protocolShortName() + { return QString("VLAN"); } + int numFields(); + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; // IP @@ -198,6 +365,10 @@ private: OstProto::Ip d; public: + virtual ~IpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + + bool update(OstProto::Ip ip) { d.MergeFrom(ip); return true; } enum IpAddrMode { IpAddrFixed, @@ -209,8 +380,8 @@ public: enum IpFlag { IpOverrideVersion = 0x01, IpOverrideHdrLen = 0x02, - IpOverrideTotLen = 0x03, - IpOverrideCksum = 0x04 + IpOverrideTotLen = 0x04, + IpOverrideCksum = 0x08 }; Q_DECLARE_FLAGS(IpFlags, IpFlag); @@ -346,14 +517,31 @@ public: bool setDstIpMask(quint32 dstIpMask) { d.set_dst_ip_mask(dstIpMask); return true; } - // TODO: Options + // TODO(LOW): Options + + + virtual QString protocolName() + { return QString("Internet Protocol version 4"); } + QString protocolShortName() + { return QString("IPv4"); } + int numFields() + { return 12; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; Q_DECLARE_OPERATORS_FOR_FLAGS(IpProtocol::IpFlags) class ArpProtocol: public AbstractProtocol { - // TODO: ARP + // TODO(LOW): ARP + OstProto::Arp d; + +public: + virtual ~ArpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Arp arp) { d.MergeFrom(arp); return true; } }; // TCP @@ -363,6 +551,11 @@ private: OstProto::Tcp d; public: + virtual ~TcpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + + bool update(OstProto::Tcp tcp) { d.MergeFrom(tcp); return true; } + enum TcpFlag { TcpOverrideHdrLen = 0x01, @@ -402,7 +595,7 @@ public: quint16 dstPort() { return d.dst_port(); } - bool setdstPort(quint16 dstPort) + bool setDstPort(quint16 dstPort) { d.set_dst_port(dstPort); return true; } quint32 seqNum() @@ -426,7 +619,7 @@ public: { d.set_hdrlen_rsvd((d.hdrlen_rsvd() & 0xF0) | rsvd); return true; } - // TODO: convert to enum maybe? + // TODO(MED): convert to enum maybe? quint8 flags() { return d.flags(); } bool setFlags(quint8 flags) @@ -448,11 +641,21 @@ public: bool setCksum(quint16 cksum) { d.set_cksum(cksum); return true; } - quint16 urg_ptr() + quint16 urgPtr() { return d.urg_ptr(); } - bool seturg_ptr(quint16 urg_ptr) + bool setUrgPtr(quint16 urg_ptr) { d.set_urg_ptr(urg_ptr); return true; } + + virtual QString protocolName() + { return QString("Transmission Control Protocol"); } + QString protocolShortName() + { return QString("TCP"); } + int numFields() + { return 10; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; Q_DECLARE_OPERATORS_FOR_FLAGS(TcpProtocol::TcpFlags) @@ -464,6 +667,11 @@ private: OstProto::Udp d; public: + virtual ~UdpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + + bool update(OstProto::Udp udp) { d.MergeFrom(udp); return true; } + enum UdpFlag { UdpOverrideTotLen = 0x01, @@ -503,7 +711,7 @@ public: quint16 dstPort() { return d.dst_port(); } - bool setdstPort(quint16 dstPort) + bool setDstPort(quint16 dstPort) { d.set_dst_port(dstPort); return true; } quint16 totLen() @@ -516,32 +724,78 @@ public: bool setCksum(quint16 cksum) { d.set_cksum(cksum); return true; } + + virtual QString protocolName() + { return QString("User Datagram Protocol"); } + QString protocolShortName() + { return QString("UDP"); } + int numFields() + { return 4; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; -class IcmpProtocol { -// TODO: ICMP +class IcmpProtocol : public AbstractProtocol +{ +// TODO(LOW): ICMP + OstProto::Icmp d; + +public: + virtual ~IcmpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Icmp icmp) { d.MergeFrom(icmp); return true; } }; -class IgmpProtocol { -// TODO: IGMP +class IgmpProtocol : public AbstractProtocol +{ +// TODO(LOW): IGMP + OstProto::Igmp d; + +public: + virtual ~IgmpProtocol() {} + virtual ::google::protobuf::Message& data() {return d;} + bool update(OstProto::Igmp igmp) { d.MergeFrom(igmp); return true; } }; class Stream { - static quint32 mAllocId; - quint32 mId; OstProto::StreamCore *mCore; - MacProtocol *mMac; - IpProtocol *mIp; + UnknownProtocol *mUnknown; + MacProtocol *mMac; + + LlcProtocol *mLlc; + SnapProtocol *mSnap; + Eth2Protocol *mEth2; + VlanProtocol *mVlan; + + IpProtocol *mIp; + ArpProtocol *mArp; + + TcpProtocol *mTcp; + UdpProtocol *mUdp; + IcmpProtocol *mIcmp; + IgmpProtocol *mIgmp; + +public: + MacProtocol* mac() { return mMac; } + + LlcProtocol* llc() { return mLlc; } + SnapProtocol* snap() { return mSnap; } + Eth2Protocol* eth2() { return mEth2; } + VlanProtocol* vlan() { return mVlan; } + + IpProtocol* ip() { return mIp; } + ArpProtocol* arp() { return mArp; } + + TcpProtocol* tcp() { return mTcp; } + UdpProtocol* udp() { return mUdp; } + IcmpProtocol* icmp() { return mIcmp; } + IgmpProtocol* igmp() { return mIgmp; } -#if 0 - friend class StreamConfigDialog; - friend class StreamModel; - friend class PacketModel; -#endif public: enum FrameType { @@ -566,17 +820,68 @@ public: e_fl_random }; + enum L3Proto { + e_l3_none, + e_l3_ip, + e_l3_arp, + }; + + enum L4Proto { + e_l4_none, + e_l4_tcp, + e_l4_udp, + e_l4_icmp, + e_l4_igmp, + }; + // ------------------------------------------------------- // Methods // ------------------------------------------------------- Stream(); + bool operator < (const Stream &s) const + { return(mCore->ordinal() < s.mCore->ordinal()); } + + bool update(OstProto::Stream *stream) + { + mCore->MergeFrom(stream->core()); + mMac->update(stream->mac()); + + mLlc->update(stream->llc()); + mSnap->update(stream->snap()); + mEth2->update(stream->eth2()); + mVlan->update(stream->vlan()); + + mIp->update(stream->ip()); + mArp->update(stream->arp()); + + mTcp->update(stream->tcp()); + mUdp->update(stream->udp()); + mIcmp->update(stream->icmp()); + mIgmp->update(stream->igmp()); + + // FIXME(MED): Re-eval why not store complete OstProto::Stream + // instead of components + return true; + } + + void getConfig(uint portId, OstProto::Stream *s); + quint32 id() { return mId;} + bool setId(quint32 id) + { mId = id; return true;} + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif quint32 ordinal() { return mCore->ordinal();} - bool setOrderdinal(quint32 ordinal) + bool setOrdinal(quint32 ordinal) { mCore->set_ordinal(ordinal); return true; } bool isEnabled() const @@ -606,6 +911,11 @@ public: bool setPattern(quint32 pattern) { mCore->set_pattern(pattern); return true; } +// TODO(HI) : ????? +#if 0 + quint16 dataStartOfs; +#endif + // Frame Length (includes CRC) FrameLengthMode lenMode() { return (FrameLengthMode) mCore->len_mode(); } @@ -628,25 +938,33 @@ public: bool setFrameLenMax(quint16 frameLenMax) { mCore->set_frame_len_max(frameLenMax); return true; } -// TODO + L3Proto l3Proto() + { return (L3Proto) mCore->l3_proto(); } + bool setL3Proto(L3Proto l3Proto) + { mCore->set_l3_proto((OstProto::StreamCore::L3Proto) l3Proto); + return true; } + + L4Proto l4Proto() + { return (L4Proto) mCore->l4_proto(); } + bool setL4Proto(L4Proto l4Proto) + { mCore->set_l4_proto((OstProto::StreamCore::L4Proto) l4Proto); + return true; } + + + //--------------------------------------------------------------- + // Methods for use by Packet Model + //--------------------------------------------------------------- + QList selectedProtocols; + + int numProtocols(); #if 0 - quint16 dataStartOfs; + int protocolId(int index); + int protocolIndex(int id); #endif - - MacProtocol* mac() { return mMac; } - IpProtocol* ip() { return mIp; } - + AbstractProtocol* protocol(int index); private: -#if 0 - void InitDefaultMeta(); - void InitDefaultProto(); - void InitDefaultL2(); - void InitDefaultL3(); - void InitDefaultL3Ip(); - void InitDefaultL4(); - void InitDefaultL4Tcp(); - void InitDefaultL4Udp(); -#endif + void updateSelectedProtocols(); + }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 8e6a8a6..a881dce 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -2,34 +2,31 @@ #include "streamconfigdialog.h" #include "stream.h" -// TODO(LOW): Remove #include "modeltest.h" -StreamConfigDialog::StreamConfigDialog(QList *streamList, - uint streamIndex, QWidget *parent) : QDialog (parent) +// TODO(HI): Write HexLineEdit::setNum() and num() and use it in +// Load/Store stream methods + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) { setupUi(this); setupUiExtra(); - // FIXME(MED): Assumption that streamlist and streamIndex are valid - mpStreamList = streamList; + //mpStreamList = streamList; mCurrentStreamIndex = streamIndex; LoadCurrentStream(); - mpPacketModel = new PacketModel(&((*mpStreamList)[mCurrentStreamIndex]), + mpPacketModel = new PacketModel(&mPort.streamByIndex(mCurrentStreamIndex), this); tvPacketTree->setModel(mpPacketModel); mpPacketModelTester = new ModelTest(mpPacketModel); tvPacketTree->header()->hide(); - qDebug("stream %p %d/%d loaded", - mpStreamList, mCurrentStreamIndex, mpStreamList->size()); -// FIXME(MED): Enable this navigation -#if 0 - pbPrev->setDisabled((currStreamIdx == 0)); - pbNext->setDisabled((currStreamIdx == 2)); -#endif + // FIXME(MED): Enable this navigation + pbPrev->setDisabled(true); + pbNext->setDisabled(true); } void StreamConfigDialog::setupUiExtra() @@ -304,7 +301,7 @@ void StreamConfigDialog::on_lePattern_editingFinished() void StreamConfigDialog::LoadCurrentStream() { - Stream *pStream = &((*mpStreamList)[mCurrentStreamIndex]); + Stream *pStream = &mPort.streamByIndex(mCurrentStreamIndex); QString str; qDebug("loading pStream %p", pStream); @@ -342,19 +339,56 @@ void StreamConfigDialog::LoadCurrentStream() break; } -// TODO + leDsap->setText(uintToHexStr(pStream->llc()->dsap(), str, 1)); + leSsap->setText(uintToHexStr(pStream->llc()->ssap(), str, 1)); + leControl->setText(uintToHexStr(pStream->llc()->ctl(), str, 1)); + leOui->setText(uintToHexStr(pStream->snap()->oui(), str, 3)); + + leType->setText(uintToHexStr(pStream->eth2()->type(), str, 2)); + + switch(pStream->l3Proto()) + { + case Stream::e_l3_none: + rbL3None->setChecked(true); + break; + case Stream::e_l3_ip: + rbL3Ipv4->setChecked(true); + break; + case Stream::e_l3_arp: + rbL3Arp->setChecked(true); + break; + default: + qDebug("%s: unknown L3 Protocol %d", __FUNCTION__, + pStream->l3Proto()); + } + + switch(pStream->l4Proto()) + { + case Stream::e_l4_none: + rbL4None->setChecked(true); + break; + case Stream::e_l4_tcp: + rbL4Tcp->setChecked(true); + break; + case Stream::e_l4_udp: + rbL4Udp->setChecked(true); + break; + case Stream::e_l4_icmp: + rbL4Icmp->setChecked(true); + break; + case Stream::e_l4_igmp: + rbL4Igmp->setChecked(true); + break; + default: + qDebug("%s: unknown l4 Protocol %d", __FUNCTION__, + pStream->l4Proto()); + } +// PB (not needed anymore?) #if 0 - leDsap->setText(uintToHexStr(pStream->proto.dsap, str, 1)); - leSsap->setText(uintToHexStr(pStream->proto.ssap, str, 1)); - leControl->setText(uintToHexStr(pStream->proto.ctl, str, 1)); - leOui->setText(uintToHexStr((pStream->proto.ouiMsb << 16 + pStream->proto.ouiLshw), str, 3)); - - leType->setText(uintToHexStr(pStream->proto.etherType, str, 2)); - // Check for specific supported protocols first ... - if (pStream->proto.etherType == ETH_TYP_IP) + if (pStream->eth2()->type() == ETH_TYP_IP) rbL3Ipv4->setChecked(TRUE); - else if (pStream->proto.etherType == ETH_TYP_ARP) + else if (pStream->eth2()->type() == ETH_TYP_ARP) rbL3Arp->setChecked(TRUE); // ... then for None/Other @@ -390,21 +424,29 @@ void StreamConfigDialog::LoadCurrentStream() cmbSrcMacMode->setCurrentIndex(pStream->mac()->srcMacMode()); leSrcMacCount->setText(str.setNum(pStream->mac()->srcMacCount())); leSrcMacStep->setText(str.setNum(pStream->mac()->srcMacStep())); -#if 0 - cmbCvlanPrio->setCurrentIndex(pStream->l2.eth.cvlanPrio); - cmbCvlanCfi->setCurrentIndex(pStream->l2.eth.cvlanCfi); - leCvlanId->setText(str.setNum(pStream->l2.eth.cvlanId)); - leCvlanTpid->setText(str.setNum(pStream->l2.eth.ctpid)); - cbCvlanTpidOverride->setChecked((pStream->l2.eth.vlanMask & VM_CVLAN_TPID_OVERRIDE) > 0); - gbCvlan->setChecked((pStream->l2.eth.vlanMask & VM_CVLAN_TAGGED) > 0); - cmbSvlanPrio->setCurrentIndex(pStream->l2.eth.svlanPrio); - cmbSvlanCfi->setCurrentIndex(pStream->l2.eth.svlanCfi); - leSvlanId->setText(str.setNum(pStream->l2.eth.svlanId)); - leSvlanTpid->setText(str.setNum(pStream->l2.eth.stpid)); - cbSvlanTpidOverride->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TPID_OVERRIDE) > 0); - gbSvlan->setChecked((pStream->l2.eth.vlanMask & VM_SVLAN_TAGGED) > 0); -#endif + { + VlanProtocol *vlan = pStream->vlan(); + VlanProtocol::VlanFlags f; + + cmbCvlanPrio->setCurrentIndex(vlan->cvlanPrio()); + cmbCvlanCfi->setCurrentIndex(vlan->cvlanCfi()); + leCvlanId->setText(str.setNum(vlan->cvlanId())); + leCvlanTpid->setText(str.setNum(vlan->ctpid())); + cbCvlanTpidOverride->setChecked(vlan->vlanFlags().testFlag( + VlanProtocol::VlanCtpidOverride)); + gbCvlan->setChecked(vlan->vlanFlags().testFlag( + VlanProtocol::VlanCvlanTagged)); + + cmbSvlanPrio->setCurrentIndex(vlan->svlanPrio()); + cmbSvlanCfi->setCurrentIndex(vlan->svlanCfi()); + leSvlanId->setText(str.setNum(vlan->svlanId())); + leSvlanTpid->setText(str.setNum(vlan->stpid())); + cbSvlanTpidOverride->setChecked(vlan->vlanFlags().testFlag( + VlanProtocol::VlanStpidOverride)); + gbSvlan->setChecked(vlan->vlanFlags().testFlag( + VlanProtocol::VlanSvlanTagged)); + } } } @@ -450,73 +492,75 @@ void StreamConfigDialog::LoadCurrentStream() // L3 | ARP { - // TODO + // TODO(LOW) } } -#if 0 // L4 { // L4 | TCP { - leTcpSrcPort->setText(str.setNum(pStream->l4.tcp.srcPort)); - leTcpDstPort->setText(str.setNum(pStream->l4.tcp.dstPort)); + leTcpSrcPort->setText(str.setNum(pStream->tcp()->srcPort())); + leTcpDstPort->setText(str.setNum(pStream->tcp()->dstPort())); - leTcpSeqNum->setText(str.setNum(pStream->l4.tcp.seqNum)); - leTcpAckNum->setText(str.setNum(pStream->l4.tcp.ackNum)); + leTcpSeqNum->setText(str.setNum(pStream->tcp()->seqNum())); + leTcpAckNum->setText(str.setNum(pStream->tcp()->ackNum())); - leTcpHdrLen->setText(str.setNum(pStream->l4.tcp.hdrLen)); - cbTcpHdrLenOverride->setChecked((pStream->l4.tcp.tcpMask & TM_OVERRIDE_HDRLEN) > 0); + leTcpHdrLen->setText(str.setNum(pStream->tcp()->hdrLen())); + cbTcpHdrLenOverride->setChecked((pStream->tcp()->tcpFlags(). + testFlag(TcpProtocol::TcpOverrideHdrLen))); - leTcpWindow->setText(str.setNum(pStream->l4.tcp.window)); + leTcpWindow->setText(str.setNum(pStream->tcp()->window())); - leTcpCksum->setText(str.setNum(pStream->l4.tcp.cksum)); - cbTcpCksumOverride->setChecked((pStream->l4.tcp.tcpMask & TM_OVERRIDE_CKSUM) > 0); + leTcpCksum->setText(str.setNum(pStream->tcp()->cksum())); + cbTcpCksumOverride->setChecked((pStream->tcp()->tcpFlags(). + testFlag(TcpProtocol::TcpOverrideCksum))); - leTcpUrgentPointer->setText(str.setNum(pStream->l4.tcp.urgPtr)); + leTcpUrgentPointer->setText(str.setNum(pStream->tcp()->urgPtr())); - cbTcpFlagsUrg->setChecked((pStream->l4.tcp.flags & TCP_FLAG_URG) > 0); - cbTcpFlagsAck->setChecked((pStream->l4.tcp.flags & TCP_FLAG_ACK) > 0); - cbTcpFlagsPsh->setChecked((pStream->l4.tcp.flags & TCP_FLAG_PSH) > 0); - cbTcpFlagsRst->setChecked((pStream->l4.tcp.flags & TCP_FLAG_RST) > 0); - cbTcpFlagsSyn->setChecked((pStream->l4.tcp.flags & TCP_FLAG_SYN) > 0); - cbTcpFlagsFin->setChecked((pStream->l4.tcp.flags & TCP_FLAG_FIN) > 0); + cbTcpFlagsUrg->setChecked((pStream->tcp()->flags() & TCP_FLAG_URG) > 0); + cbTcpFlagsAck->setChecked((pStream->tcp()->flags() & TCP_FLAG_ACK) > 0); + cbTcpFlagsPsh->setChecked((pStream->tcp()->flags() & TCP_FLAG_PSH) > 0); + cbTcpFlagsRst->setChecked((pStream->tcp()->flags() & TCP_FLAG_RST) > 0); + cbTcpFlagsSyn->setChecked((pStream->tcp()->flags() & TCP_FLAG_SYN) > 0); + cbTcpFlagsFin->setChecked((pStream->tcp()->flags() & TCP_FLAG_FIN) > 0); } // L4 | UDP { - leUdpSrcPort->setText(str.setNum(pStream->l4.udp.srcPort)); - leUdpDstPort->setText(str.setNum(pStream->l4.udp.dstPort)); + leUdpSrcPort->setText(str.setNum(pStream->udp()->srcPort())); + leUdpDstPort->setText(str.setNum(pStream->udp()->dstPort())); - leUdpLength->setText(str.setNum(pStream->l4.udp.totLen)); - cbUdpLengthOverride->setChecked((pStream->l4.udp.udpMask & UM_OVERRIDE_TOTLEN) > 0); + leUdpLength->setText(str.setNum(pStream->udp()->totLen())); + cbUdpLengthOverride->setChecked((pStream->udp()->udpFlags(). + testFlag(UdpProtocol::UdpOverrideTotLen))); - leUdpCksum->setText(str.setNum(pStream->l4.udp.cksum)); - cbUdpCksumOverride->setChecked((pStream->l4.udp.udpMask & UM_OVERRIDE_CKSUM) > 0); + + leUdpCksum->setText(str.setNum(pStream->udp()->cksum())); + cbUdpCksumOverride->setChecked((pStream->udp()->udpFlags(). + testFlag(UdpProtocol::UdpOverrideCksum))); } // L4 | ICMP { - // TODO + // TODO(LOW) } // L4 | IGMP { - // TODO + // TODO(LOW) } } -#endif } void StreamConfigDialog::StoreCurrentStream() { - Stream *pStream = &(*mpStreamList)[mCurrentStreamIndex]; + Stream *pStream = &mPort.streamByIndex(mCurrentStreamIndex); QString str; bool isOk; qDebug("storing pStream %p", pStream); -#if 1 // FIXME: Temp till we use protobuff accessors // Meta Data pStream->setPatternMode((Stream::DataPatternMode) cmbPatternMode->currentIndex()); pStream->setPattern(lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); @@ -525,7 +569,7 @@ void StreamConfigDialog::StoreCurrentStream() pStream->setFrameLen(lePktLen->text().toULong(&isOk)); pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); -#endif + // Protocols { if (rbFtNone->isChecked()) @@ -540,28 +584,39 @@ void StreamConfigDialog::StoreCurrentStream() pStream->setFrameType(Stream::e_ft_snap); qDebug("store ft(%d)\n", pStream->frameType()); -#if 0 - pStream->proto.dsap = leDsap->text().remove(QChar(' ')).toULong(&isOk, 16); - pStream->proto.ssap = leSsap->text().remove(QChar(' ')).toULong(&isOk, 16); - pStream->proto.ctl = leControl->text().remove(QChar(' ')).toULong(&isOk, 16); - pStream->proto.ouiMsb = leOui->text().remove(QChar(' ')).toULong(&isOk, 16) >> 16; - pStream->proto.ouiLshw = 0x0000FFFF & leOui->text().remove(QChar(' ')).toULong(&isOk, 16); - pStream->proto.etherType = leType->text().remove(QChar(' ')).toULong(&isOk, 16); + pStream->llc()->setDsap(leDsap->text().remove(QChar(' ')).toULong(&isOk, 16)); + pStream->llc()->setSsap(leSsap->text().remove(QChar(' ')).toULong(&isOk, 16)); + pStream->llc()->setCtl(leControl->text().remove(QChar(' ')).toULong(&isOk, 16)); + pStream->snap()->setOui(leOui->text().remove(QChar(' ')).toULong(&isOk, 16)); + pStream->eth2()->setType(leType->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->proto.ipProto = leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16); - - // Just check for None/Other - no need to do anything for specific supported protocols - pStream->proto.protoMask = 0; if (rbL3None->isChecked()) - pStream->proto.protoMask |= PM_L3_PROTO_NONE; - else if (rbL3Other->isChecked()) - pStream->proto.protoMask |= PM_L3_PROTO_OTHER; + pStream->setL3Proto(Stream::e_l3_none); + else if (rbL3Ipv4->isChecked()) + pStream->setL3Proto(Stream::e_l3_ip); + else if (rbL3Arp->isChecked()) + pStream->setL3Proto(Stream::e_l3_arp); + else + { + qCritical("No L3 Protocol??? Problem in Code!!!"); + pStream->setL3Proto(Stream::e_l3_none); + } if (rbL4None->isChecked()) - pStream->proto.protoMask |= PM_L4_PROTO_NONE; - else if (rbL4Other->isChecked()) - pStream->proto.protoMask |= PM_L4_PROTO_OTHER; -#endif + pStream->setL4Proto(Stream::e_l4_none); + else if (rbL4Tcp->isChecked()) + pStream->setL4Proto(Stream::e_l4_tcp); + else if (rbL4Udp->isChecked()) + pStream->setL4Proto(Stream::e_l4_udp); + else if (rbL4Icmp->isChecked()) + pStream->setL4Proto(Stream::e_l4_icmp); + else if (rbL4Igmp->isChecked()) + pStream->setL4Proto(Stream::e_l4_igmp); + else + { + qCritical("No L4 Protocol??? Problem in Code!!!"); + pStream->setL4Proto(Stream::e_l4_none); + } } // L2 @@ -587,27 +642,30 @@ void StreamConfigDialog::StoreCurrentStream() leSrcMacStep->text().toULong(&isOk)); -#if 0 - pStream->l2.eth.vlanMask = 0; + { + VlanProtocol *vlan = pStream->vlan(); + VlanProtocol::VlanFlags f = 0; - pStream->l2.eth.cvlanPrio = cmbCvlanPrio->currentIndex(); - pStream->l2.eth.cvlanCfi = cmbCvlanCfi->currentIndex(); - pStream->l2.eth.cvlanId = leCvlanId->text().toULong(&isOk); - pStream->l2.eth.ctpid = leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk); - if (cbCvlanTpidOverride->isChecked()) - pStream->l2.eth.vlanMask |= VM_CVLAN_TPID_OVERRIDE; - if (gbCvlan->isChecked()) - pStream->l2.eth.vlanMask |= VM_CVLAN_TAGGED; + vlan->setCvlanPrio(cmbCvlanPrio->currentIndex()); + vlan->setCvlanCfi(cmbCvlanCfi->currentIndex()); + vlan->setCvlanId(leCvlanId->text().toULong(&isOk)); + vlan->setCtpid(leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk)); + if (cbCvlanTpidOverride->isChecked()) + f |= VlanProtocol::VlanCtpidOverride; + if (gbCvlan->isChecked()) + f |= VlanProtocol::VlanCvlanTagged; - pStream->l2.eth.svlanPrio = cmbSvlanPrio->currentIndex(); - pStream->l2.eth.svlanCfi = cmbSvlanCfi->currentIndex(); - pStream->l2.eth.svlanId = leSvlanId->text().toULong(&isOk); - pStream->l2.eth.stpid = leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk); - if (cbSvlanTpidOverride->isChecked()) - pStream->l2.eth.vlanMask |= VM_SVLAN_TPID_OVERRIDE; - if (gbSvlan->isChecked()) - pStream->l2.eth.vlanMask |= VM_SVLAN_TAGGED; -#endif + vlan->setSvlanPrio(cmbSvlanPrio->currentIndex()); + vlan->setSvlanCfi(cmbSvlanCfi->currentIndex()); + vlan->setSvlanId(leSvlanId->text().toULong(&isOk)); + vlan->setStpid(leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk)); + if (cbSvlanTpidOverride->isChecked()) + f |= VlanProtocol::VlanStpidOverride; + if (gbSvlan->isChecked()) + f |= VlanProtocol::VlanSvlanTagged; + + vlan->setVlanFlags(f); + } } } @@ -616,7 +674,8 @@ void StreamConfigDialog::StoreCurrentStream() // L3 | IP { IpProtocol *ip = pStream->ip(); - IpProtocol::IpFlags f; + IpProtocol::IpFlags f = 0; + int ff = 0; ip->setVer(leIpVersion->text().toULong(&isOk)); if (cbIpVersionOverride->isChecked()) @@ -634,7 +693,6 @@ void StreamConfigDialog::StoreCurrentStream() ip->setId(leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); ip->setFragOfs(leIpFragOfs->text().toULong(&isOk)); - int ff; if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; ip->setFlags(ff); @@ -661,71 +719,79 @@ void StreamConfigDialog::StoreCurrentStream() // L3 | ARP { - // TODO + // TODO(LOW) } } -// TODO -#if 0 // L4 { // L4 | TCP { - pStream->l4.tcp.tcpMask = 0; + TcpProtocol *tcp = pStream->tcp(); + TcpProtocol::TcpFlags f = 0; + int ff = 0; - pStream->l4.tcp.srcPort = leTcpSrcPort->text().toULong(&isOk); - pStream->l4.tcp.dstPort = leTcpDstPort->text().toULong(&isOk); + tcp->setSrcPort(leTcpSrcPort->text().toULong(&isOk)); + tcp->setDstPort(leTcpDstPort->text().toULong(&isOk)); - pStream->l4.tcp.seqNum = leTcpSeqNum->text().toULong(&isOk); - pStream->l4.tcp.ackNum = leTcpAckNum->text().toULong(&isOk); + tcp->setSeqNum(leTcpSeqNum->text().toULong(&isOk)); + tcp->setAckNum(leTcpAckNum->text().toULong(&isOk)); - pStream->l4.tcp.hdrLen = leTcpHdrLen->text().toULong(&isOk); + tcp->setHdrLen(leTcpHdrLen->text().toULong(&isOk)); if (cbTcpHdrLenOverride->isChecked()) - pStream->l4.tcp.tcpMask |= TM_OVERRIDE_HDRLEN; + f |= TcpProtocol::TcpOverrideHdrLen; - pStream->l4.tcp.window = leTcpWindow->text().toULong(&isOk); + tcp->setWindow(leTcpWindow->text().toULong(&isOk)); - pStream->l4.tcp.cksum = leTcpCksum->text().remove(QChar(' ')).toULong(&isOk); + tcp->setCksum(leTcpCksum->text().remove(QChar(' ')).toULong(&isOk)); if (cbTcpCksumOverride->isChecked()) - pStream->l4.tcp.tcpMask |= TM_OVERRIDE_CKSUM; + f |= TcpProtocol::TcpOverrideCksum; - pStream->l4.tcp.urgPtr = leTcpUrgentPointer->text().toULong(&isOk); + tcp->setUrgPtr(leTcpUrgentPointer->text().toULong(&isOk)); - pStream->l4.tcp.flags = 0; - if (cbTcpFlagsUrg->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_URG; - if (cbTcpFlagsAck->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_ACK; - if (cbTcpFlagsPsh->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_PSH; - if (cbTcpFlagsRst->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_RST; - if (cbTcpFlagsSyn->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_SYN; - if (cbTcpFlagsFin->isChecked()) pStream->l4.tcp.flags |= TCP_FLAG_FIN; + if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + tcp->setFlags(ff); + + tcp->setTcpFlags(f); } // L4 | UDP { - pStream->l4.udp.udpMask = 0; + UdpProtocol *udp = pStream->udp(); + UdpProtocol::UdpFlags f = 0; - pStream->l4.udp.srcPort = leUdpSrcPort->text().toULong(&isOk); - pStream->l4.udp.dstPort = leUdpDstPort->text().toULong(&isOk); + udp->setSrcPort(leUdpSrcPort->text().toULong(&isOk)); + udp->setDstPort(leUdpDstPort->text().toULong(&isOk)); - pStream->l4.udp.totLen = leUdpLength->text().toULong(&isOk); - if (cbUdpLengthOverride->isChecked()) pStream->l4.udp.udpMask |= UM_OVERRIDE_TOTLEN; + udp->setTotLen(leUdpLength->text().toULong(&isOk)); - pStream->l4.udp.cksum = leUdpCksum->text().remove(QChar(' ')).toULong(&isOk); - if (cbUdpCksumOverride->isChecked()) pStream->l4.udp.udpMask |= UM_OVERRIDE_CKSUM; + if (cbUdpLengthOverride->isChecked()) + f |= UdpProtocol::UdpOverrideTotLen; + + udp->setCksum(leUdpCksum->text().remove(QChar(' ')).toULong(&isOk)); + if (cbUdpCksumOverride->isChecked()) + f |= UdpProtocol::UdpOverrideCksum; + + udp->setUdpFlags(f); } // L4 | ICMP { - // TODO + // TODO)(LOW) } // L4 | IGMP { - // TODO + // TODO(LOW) } } -#endif } + void StreamConfigDialog::on_pbOk_clicked() { // Store dialog contents into stream diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 1d3c99b..2485011 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -3,6 +3,7 @@ #include #include "ui_streamconfigdialog.h" +#include "port.h" #include "stream.h" #include "packetmodel.h" #include "modeltest.h" @@ -17,16 +18,18 @@ ** */ + class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog { Q_OBJECT public: - StreamConfigDialog(QList *streamList, uint streamIndex, - QWidget *parent = 0); + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); ~StreamConfigDialog(); private: - QList *mpStreamList; + //QList *mpStreamList; + + Port& mPort; uint mCurrentStreamIndex; PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index a9c1ba4..b568d52 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -191,7 +191,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 1 + 0 @@ -390,6 +390,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + Other diff --git a/client/streammodel.cpp b/client/streammodel.cpp index d56f4ee..3ee9136 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -15,7 +15,7 @@ int StreamModel::rowCount(const QModelIndex &parent) const return 0; if (mCurrentPort) - return mCurrentPort->mStreams.size(); + return mCurrentPort->numStreams(); else return 0; } @@ -53,7 +53,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const return QVariant(); // Check for row/column limits - if (index.row() >= mCurrentPort->mStreams.size()) + if (index.row() >= mCurrentPort->numStreams()) return QVariant(); if (index.column() >= StreamMaxFields) @@ -80,7 +80,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const case StreamName: { if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->mStreams[index.row()].name(); + return mCurrentPort->streamByIndex(index.row()).name(); else return QVariant(); break; @@ -94,7 +94,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const #endif if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) { - if (mCurrentPort->mStreams[index.row()].isEnabled()) + if (mCurrentPort->streamByIndex(index.row()).isEnabled()) return Qt::Checked; else return Qt::Unchecked; @@ -121,11 +121,11 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r switch (index.column()) { case StreamName: - mCurrentPort->mStreams[index.row()].setName(value.toString()); + mCurrentPort->streamByIndex(index.row()).setName(value.toString()); return true; break; case StreamStatus: - mCurrentPort->mStreams[index.row()].setIsEnabled(value.toBool()); + mCurrentPort->streamByIndex(index.row()).setIsEnabled(value.toBool()); return true; break; @@ -178,7 +178,7 @@ bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) qDebug("insertRows() count = %d", count); beginInsertRows(QModelIndex(), row, row+count-1); for (int i = 0; i < count; i++) - mCurrentPort->mStreams.insert(row, Stream()); + mCurrentPort->newStreamAt(row); endInsertRows(); return true; @@ -191,8 +191,7 @@ bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) beginRemoveRows(QModelIndex(), row, row+count-1); for (int i = 0; i < count; i++) { - // FIXME(HIGH): do we need to free the removed stream? - mCurrentPort->mStreams.removeAt(row); + mCurrentPort->deleteStreamAt(row); } endRemoveRows(); diff --git a/client/streammodel.h b/client/streammodel.h index de648a2..697517d 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -37,9 +37,11 @@ class StreamModel : public QAbstractTableModel bool removeRows (int row, int count, const QModelIndex & parent = QModelIndex()); +#if 0 // CleanedUp! // FIXME(HIGH): This *is* like a kludge QList* currentPortStreamList() { return &mCurrentPort->mStreams; } +#endif public slots: void setCurrentPortIndex(const QModelIndex ¤t); diff --git a/common/protocol.proto b/common/protocol.proto index 4fa777d..e620b2d 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -151,18 +151,18 @@ message StreamCore { enum L3Proto { e_l3_none = 0; - e_l3_other = 1; - e_l3_ip = 2; - e_l3_arp = 3; + e_l3_ip = 1; + e_l3_arp = 2; + //e_l3_other = 3; } enum L4Proto { e_l4_none = 0; - e_l4_other = 1; - e_l4_tcp = 2; - e_l4_udp = 3; - e_l4_icmp = 4; - e_l4_igmp = 5; + e_l4_tcp = 1; + e_l4_udp = 2; + e_l4_icmp = 3; + e_l4_igmp = 4; + //e_l4_other = 5; } enum DataPatternMode { @@ -202,20 +202,21 @@ message StreamCore { } message StreamId { - required uint32 port_id = 1; - required uint32 stream_id = 2; + required uint32 id = 1; } message Stream { - required StreamId id = 1; + required StreamId stream_id = 1; optional StreamCore core = 2; // Protocol data - L2 optional Mac mac = 51; + optional Llc llc = 52; optional Snap snap = 53; optional Eth2 eth2 = 54; + optional Vlan vlan = 55; // Protocol data - L3 optional Ip ip = 61; @@ -237,17 +238,22 @@ message Ack { // TODO } +message PortId { + required uint32 id = 1; +} + message PortIdList { - repeated uint32 port_id = 1; + repeated PortId port_id = 1; } message StreamIdList { - repeated StreamId id = 1; + required PortId port_id = 1; + repeated StreamId stream_id = 2; } -message PortConfig { - required uint32 port_id = 1; +message Port { + required PortId port_id = 1; optional string name = 2; optional string description = 3; optional bool is_enabled = 4; @@ -256,11 +262,12 @@ message PortConfig { } message PortConfigList { - repeated PortConfig list = 1; + repeated Port port = 1; } message StreamConfigList { - repeated Stream stream = 1; + required PortId port_id = 1; + repeated Stream stream = 2; } message CaptureBuffer { @@ -283,7 +290,7 @@ service OstService { rpc getPortIdList(Void) returns (PortIdList); rpc getPortConfig(PortIdList) returns (PortConfigList); - rpc getStreamIdList(PortIdList) returns (StreamIdList); + rpc getStreamIdList(PortId) returns (StreamIdList); rpc getStreamConfig(StreamIdList) returns (StreamConfigList); rpc addStream(StreamIdList) returns (Ack); rpc deleteStream(StreamIdList) returns (Ack); diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index 19e3daf..21afefa 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -9,15 +9,100 @@ class PbHelper { public: + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + bool update( ::google::protobuf::Message *target, ::google::protobuf::Message *source) { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 ::google::protobuf::Message::Reflection *sourceRef; ::google::protobuf::Message::Reflection *targetRef; std::vector srcFieldList; - qDebug("In %s", __FUNCTION__); if (source->GetDescriptor()->full_name() != target->GetDescriptor()->full_name()) @@ -54,13 +139,10 @@ public: break; } } - - return true; - _error_exit: qDebug("%s: error!", __FUNCTION__); return false; +#endif } - }; #endif diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 0d6226b..366ca6f 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -65,12 +65,22 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char msg[1024]; // FIXME: hardcoding + char msg[4096]; // FIXME: hardcoding char *p = (char *)&msg; int len; qDebug("In %s", __FUNCTION__); + if (!req->IsInitialized()) + { + qDebug("RpcChannel: missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + pendingMethodId = method->index(); this->controller=controller; this->done=done; @@ -91,7 +101,7 @@ void PbRpcChannel::CallMethod( *((quint16*)(p+4)) = HTONS(len); // len qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, len+8, - req->ShortDebugString().c_str()); + req->DebugString().c_str()); BUFDUMP(msg, len+8); mpSocket->write(msg, len + 8); @@ -99,7 +109,7 @@ void PbRpcChannel::CallMethod( void PbRpcChannel::on_mpSocket_readyRead() { - char msg[1024]; // FIXME: hardcoding; + char msg[4096]; // FIXME: hardcoding; char *p = (char*)&msg; int msgLen; quint16 type, method, len, rsvd; @@ -146,6 +156,13 @@ void PbRpcChannel::on_mpSocket_readyRead() qDebug("client(%s): Parsed as %s", __FUNCTION__, response->DebugString().c_str()); + if (!response->IsInitialized()) + { + qDebug("RpcChannel: missing required fields in response"); + qDebug(response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } pendingMethodId = -1; controller = NULL; diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 45555bf..7b6335b 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -37,7 +37,7 @@ bool RpcServer::registerService(::google::protobuf::Service *service, void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) { - char msg[1024]; // FIXME: hardcoding + char msg[4096]; // FIXME: hardcoding char *p = (char *)&msg; int len; @@ -50,6 +50,13 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo goto _exit; } + if (!resp->IsInitialized()) + { + qDebug("response missing required fields!!"); + qDebug(resp->InitializationErrorString().c_str()); + goto _exit; + } + *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_RESPONSE); // type TODO:RESPONSE *((quint16*)(p+2)) = HTONS(pendingMethodId); // method *((quint16*)(p+6)) = HTONS(0); // rsvd @@ -60,9 +67,9 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo len = resp->ByteSize(); (*(quint16*)(p+4)) = HTONS(len); // len - qDebug("Server(%s): sending %d bytest to client encoding <%s>", + qDebug("Server(%s): sending %d bytes to client encoding <%s>", __FUNCTION__, len + 8, resp->DebugString().c_str()); - BUFDUMP(msg, len + 8); + //BUFDUMP(msg, len + 8); clientSock->write(msg, len + 8); @@ -116,7 +123,7 @@ void RpcServer::when_error(QAbstractSocket::SocketError socketError) void RpcServer::when_dataAvail() { - char msg[1024]; // FIXME: hardcoding; + char msg[4096]; // FIXME: hardcoding; int msgLen; char *p = (char*) &msg; quint16 type, method, len, rsvd; @@ -128,13 +135,14 @@ void RpcServer::when_dataAvail() LogInt(QString(QByteArray(msg, msgLen).toHex())); qDebug("Server %s: rcvd %d bytes", __FUNCTION__, msgLen); - BUFDUMP(msg, msgLen); + //BUFDUMP(msg, msgLen); type = NTOHS(GET16(p+0)); - qDebug("GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), type); method = NTOHS(GET16(p+2)); len = NTOHS(GET16(p+4)); rsvd = NTOHS(GET16(p+6)); + qDebug("type = %d, method = %d, len = %d, rsvd = %d", + type, method, len, rsvd); if (type != PB_MSG_TYPE_REQUEST) { @@ -165,9 +173,22 @@ void RpcServer::when_dataAvail() // Serialized data starts from offset 8 req->ParseFromArray((void*) (msg+8), len); + if (!req->IsInitialized()) + { + qDebug("Missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); + delete req; + delete resp; + + goto _error_exit; + } + qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + resp->DebugString().c_str()); controller = new PbRpcController; + qDebug("before service->callmethod()"); + service->CallMethod(methodDesc, controller, req, resp, NewCallback(this, &RpcServer::done, resp, controller)); diff --git a/server/myservice.cpp b/server/myservice.cpp index 93e625c..de438e1 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -3,6 +3,28 @@ #define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} +int MyService::getStreamIndex(unsigned int portIdx, + unsigned int streamId) +{ + int i; + + // note: index and id are interchageable for port but not for stream + + Q_ASSERT(portIdx < numPorts); + + for (i = 0; i < portInfo[portIdx].streamList.size(); i++) + { + if (streamId == portInfo[portIdx].streamList.at(i).d.stream_id().id()) + goto _found; + } + + qDebug("%s: stream id %d not found", __PRETTY_FUNCTION__, streamId); + return -1; + +_found: + return i; +} + MyService::MyService(AbstractHost *host) { pcap_if_t *dev; @@ -24,18 +46,12 @@ MyService::MyService(AbstractHost *host) /* Count number of local ports */ for(dev = alldevs; dev != NULL; dev = dev->next) numPorts++; - + portInfo = new PortInfo[numPorts]; /* Populate and Print the list */ - for(i=0, dev=alldevs; dev!=NULL; i++, dev=dev->next) + for(i=0, dev=alldevs; (i < numPorts) && (dev!=NULL); i++, dev=dev->next) { -#if 0 // PB - //portInfo[i].portId = i; - //portInfo[i].dev = dev; - //portInfo[i].streamHead = NULL; - //portInfo[i].streamTail = NULL; -#endif portInfo[i].setId(i); portInfo[i].setPcapDev(dev); #if 1 @@ -44,8 +60,17 @@ MyService::MyService(AbstractHost *host) { LOG(" (%s)\n", dev->description); } - else - LOG(" (No description available)\n"); +#endif +#if 0 + // FIXME(HI): Testing only!!!! + { + StreamInfo s; + + s.d.mutable_stream_id()->set_id(0); + portInfo[i].streamList.append(s); + s.d.mutable_stream_id()->set_id(1); + portInfo[i].streamList.append(s); + } #endif } @@ -61,11 +86,7 @@ _fail: MyService::~MyService() { - unsigned int i; -#if 0 // PB????? - for (i = 0; i < numPorts; i++) - DeleteAllStreams(i); -#endif + delete portInfo; pcap_freealldevs(alldevs); } @@ -75,14 +96,15 @@ void MyService::getPortIdList( ::OstProto::PortIdList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); for (uint i = 0; i < numPorts; i++) - response->add_port_id(portInfo[i].d.port_id()); + { + ::OstProto::PortId *p; - qDebug("Server(%s): portid count = %d", __FUNCTION__, response->port_id_size()); - - qDebug("Server(%s): %s", __FUNCTION__, response->DebugString().c_str()); + p = response->add_port_id(); + p->set_id(portInfo[i].d.port_id().id()); + } done->Run(); } @@ -92,19 +114,19 @@ const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); for (int i=0; i < request->port_id_size(); i++) { - unsigned int id; + unsigned int idx; - id = request->port_id(i); - if (id < numPorts) + idx = request->port_id(i).id(); + if (idx < numPorts) { - OstProto::PortConfig *p; + OstProto::Port *p; - p = response->add_list(); - p->CopyFrom(portInfo[request->port_id(i)].d); + p = response->add_port(); + p->CopyFrom(portInfo[idx].d); } } @@ -112,34 +134,34 @@ const ::OstProto::PortIdList* request, } void MyService::getStreamIdList(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, +const ::OstProto::PortId* request, ::OstProto::StreamIdList* response, ::google::protobuf::Closure* done) { + unsigned int portIdx; - for (int i = 0; i < request->port_id_size(); i++) + qDebug("In %s", __PRETTY_FUNCTION__); + + portIdx = request->id(); + if (portIdx >= numPorts) { - unsigned int portId; - - portId = request->port_id(i); - if (portId >= numPorts) - { - qDebug("%s: Invalid port id %d", __FUNCTION__, portId); - continue; // TODO: Partial status of RPC - } - - for (int j = 0; j < portInfo[portId].streamList.size(); j++) - { - OstProto::StreamId *s, *q; - - q = portInfo[portId].streamList[j].d.mutable_id(); - assert(q->port_id() == portId); - - s = response->add_id(); - s->set_port_id(portId); - s->set_stream_id(q->stream_id()); - } + qDebug("%s: Invalid port id %d", __PRETTY_FUNCTION__, portIdx); + controller->SetFailed("Invalid Port Id"); + goto _exit; // TODO(LOW): Partial status of RPC } + + response->mutable_port_id()->set_id(portIdx); + for (int j = 0; j < portInfo[portIdx].streamList.size(); j++) + { + OstProto::StreamId *s, *q; + + q = portInfo[portIdx].streamList[j].d.mutable_stream_id(); + + s = response->add_stream_id(); + s->CopyFrom(*q); + } + +_exit: done->Run(); } @@ -148,43 +170,32 @@ const ::OstProto::StreamIdList* request, ::OstProto::StreamConfigList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + unsigned int portIdx; - for (int i = 0; i < request->id_size(); i++) + qDebug("In %s", __PRETTY_FUNCTION__); + + portIdx = request->port_id().id(); + if (portIdx >= numPorts) { - unsigned int portId; - unsigned int streamId; - - portId = request->id(i).port_id(); - if (portId >= numPorts) - { - qDebug("%s: Invalid port id %d", __FUNCTION__, portId); - continue; // TODO: Partial status of RPC - } - - streamId = request->id(i).stream_id(); - if (streamId >= numPorts) - { - qDebug("%s: Invalid port id %d", __FUNCTION__, portId); - continue; // TODO: Partial status of RPC - } - - for (int j = 0; j < portInfo[portId].streamList.size(); j++) - { - OstProto::Stream *s, *q; - -#if 0 - q = portInfo[portId].streamList[j].d.e_stream(); - assert(q->port_id() == portId); - - s = response->add_stream(); - s->set_port_id(portId); - s->set_stream_id(q->stream_id()); -#endif - // TODO: more params - } + controller->SetFailed("invalid portid"); + goto _exit; } - controller->SetFailed("Not Implemented"); + + response->mutable_port_id()->set_id(portIdx); + for (int i = 0; i < request->stream_id_size(); i++) + { + int streamIndex; + OstProto::Stream *s; + + streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); + if (streamIndex < 0) + continue; // TODO(LOW): Partial status of RPC + + s = response->add_stream(); + s->CopyFrom(portInfo[portIdx].streamList[streamIndex].d); + } + +_exit: done->Run(); } @@ -193,8 +204,36 @@ const ::OstProto::StreamIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); - controller->SetFailed("Not Implemented"); + unsigned int portIdx; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portIdx = request->port_id().id(); + if (portIdx >= numPorts) + { + controller->SetFailed("invalid portid"); + goto _exit; + } + + for (int i = 0; i < request->stream_id_size(); i++) + { + int streamIndex; + StreamInfo s; + + // If stream with same id as in request exists already ==> error!! + streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); + if (streamIndex >= 0) + continue; // TODO(LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + s.d.mutable_stream_id()->CopyFrom(request->stream_id(i)); + portInfo[portIdx].streamList.append(s); + + // TODO(LOW): fill-in response "Ack"???? + } +_exit: done->Run(); } @@ -203,8 +242,31 @@ const ::OstProto::StreamIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); - controller->SetFailed("Not Implemented"); + unsigned int portIdx; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portIdx = request->port_id().id(); + if (portIdx >= numPorts) + { + controller->SetFailed("invalid portid"); + goto _exit; + } + + for (int i = 0; i < request->stream_id_size(); i++) + { + int streamIndex; + StreamInfo s; + + streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); + if (streamIndex < 0) + continue; // TODO(LOW): Partial status of RPC + + portInfo[portIdx].streamList.removeAt(streamIndex); + + // TODO(LOW): fill-in response "Ack"???? + } +_exit: done->Run(); } @@ -213,8 +275,32 @@ const ::OstProto::StreamConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); - controller->SetFailed("Not Implemented"); + unsigned int portIdx; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portIdx = request->port_id().id(); + if (portIdx >= numPorts) + { + controller->SetFailed("invalid portid"); + goto _exit; + } + + for (int i = 0; i < request->stream_size(); i++) + { + int streamIndex; + + streamIndex = getStreamIndex(portIdx, + request->stream(i).stream_id().id()); + if (streamIndex < 0) + continue; // TODO(LOW): Partial status of RPC + + portInfo[portIdx].streamList[streamIndex].d.MergeFrom( + request->stream(i)); + + // TODO(LOW): fill-in response "Ack"???? + } +_exit: done->Run(); } @@ -223,7 +309,7 @@ const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -233,7 +319,7 @@ const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -243,7 +329,7 @@ const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -253,7 +339,7 @@ const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -263,7 +349,7 @@ const ::OstProto::PortIdList* request, ::OstProto::CaptureBufferList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -273,7 +359,7 @@ const ::OstProto::PortIdList* request, ::OstProto::PortStatsList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } @@ -283,7 +369,7 @@ const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); controller->SetFailed("Not Implemented"); done->Run(); } diff --git a/server/myservice.h b/server/myservice.h index 83ebc0f..45577be 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -12,6 +12,8 @@ #include #include +#include "../rpc/pbhelper.h" + #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 @@ -23,21 +25,7 @@ class StreamInfo OstProto::Stream d; -#if 0 // PB - 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; -#endif + StreamInfo() { PbHelper pbh; pbh.ForceSetSingularDefault(&d); } }; @@ -45,28 +33,23 @@ class PortInfo { friend class MyService; - OstProto::PortConfig d; + OstProto::Port d; pcap_if_t *dev; + /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ QList streamList; -#if 0 // PB - unsigned int portId; // FIXME:need? - Stream *streamHead; - Stream *streamTail; -#endif - public: // TODO(LOW): Both setId and setPcapDev() should together form the ctor - void setId(unsigned int id) { d.set_port_id(id); } + void setId(unsigned int id) { d.mutable_port_id()->set_id(id); } void setPcapDev(pcap_if_t *dev) { this->dev = dev; - d.set_name("eth"); // FIXME: suffix portid + d.set_name("eth"); // FIXME(MED): suffix portid d.set_description(dev->description); - d.set_is_enabled(true); // FIXME:check - d.set_is_oper_up(true); // FIXME:check - d.set_is_exclusive_control(false); // FIXME: check + d.set_is_enabled(true); // FIXME(MED):check + d.set_is_oper_up(true); // FIXME(MED):check + d.set_is_exclusive_control(false); // FIXME(MED): check } }; @@ -75,72 +58,74 @@ class MyService: public OstProto::OstService AbstractHost *host; char logStr[1024]; - unsigned numPorts; + uint numPorts; + /*! PortInfo::d::port_id and index into portInfo[] are same! */ PortInfo *portInfo; pcap_if_t *alldevs; + int getStreamIndex(unsigned int portIdx,unsigned int streamId); + public: MyService(AbstractHost* host); virtual ~MyService(); - //static const ::google::protobuf::ServiceDescriptor* descriptor(); - + /* Methods provided by the service */ virtual void getPortIdList(::google::protobuf::RpcController* controller, - const ::OstProto::Void* request, - ::OstProto::PortIdList* response, - ::google::protobuf::Closure* done); + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); virtual void getPortConfig(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortConfigList* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); virtual void getStreamIdList(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::StreamIdList* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); virtual void getStreamConfig(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::StreamConfigList* response, - ::google::protobuf::Closure* done); + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); virtual void addStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void deleteStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void modifyStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamConfigList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void startTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void stopTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void startCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void stopCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::CaptureBufferList* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::CaptureBufferList* response, + ::google::protobuf::Closure* done); virtual void getStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortStatsList* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); virtual void clearStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); }; #endif diff --git a/server/rxtx.cpp b/server/rxtx.cpp index f4b5308..11789f1 100644 --- a/server/rxtx.cpp +++ b/server/rxtx.cpp @@ -1,5 +1,5 @@ -File Not used anymore +FIXME(HI): File Not used anymore #if 0 #include "qtglobal" // FIXME: needed only for qdebug diff --git a/server/rxtx.h b/server/rxtx.h index fac9331..7b86991 100644 --- a/server/rxtx.h +++ b/server/rxtx.h @@ -1,5 +1,5 @@ -File not used anymore +FIXME(HI): File not used anymore #if 0 From 9e7b3239730c221231952b2699cc675fb1bc7340 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 30 Aug 2008 08:49:08 +0000 Subject: [PATCH 008/294] Rewrote DumpView as a subclass of QAbstractItemView (as it should be). Correspondingly reworked PacketModel to work with DumpView. Added Dot3Protocol and PayloadProtocol to Stream class --- client/dumpview.cpp | 265 +++++++++-- client/dumpview.h | 34 +- client/packetmodel.cpp | 816 +--------------------------------- client/stream.cpp | 356 ++++++++++++++- client/stream.h | 83 +++- client/streamconfigdialog.cpp | 12 +- client/streamconfigdialog.ui | 5 +- common/protocol.proto | 4 +- 8 files changed, 718 insertions(+), 857 deletions(-) diff --git a/client/dumpview.cpp b/client/dumpview.cpp index a238e95..79edab9 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -1,11 +1,10 @@ #include "dumpview.h" +//public: DumpView::DumpView(QWidget *parent) { int w, h; - data.resize(73); - // NOTE: Monospaced fonts only !!!!!!!!!!! setFont(QFont("Courier")); w = fontMetrics().width('X'); @@ -17,6 +16,7 @@ DumpView::DumpView(QWidget *parent) mSelectedRow = mSelectedCol = -1; // calculate width for offset column and the whitespace that follows it + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ mOffsetPaneTopRect = QRect(0, 0, w*4, h); mDumpPaneTopRect = QRect(mOffsetPaneTopRect.right()+w*3, 0, w*((8*3-1)+2+(8*3-1)), h); @@ -25,15 +25,10 @@ DumpView::DumpView(QWidget *parent) qDebug("DumpView::DumpView"); } +QModelIndex DumpView::indexAt( const QPoint &point ) const +{ #if 0 -QSize DumpView::sizeHint() const -{ -} -#endif - -void DumpView::mousePressEvent(QMouseEvent *event) -{ - int x = event->x(); + int x = point.x(); int row, col; if (x > mAsciiPaneTopRect.left()) @@ -48,12 +43,12 @@ void DumpView::mousePressEvent(QMouseEvent *event) { col = (x - mDumpPaneTopRect.left()) / (mCharWidth*3); } - row = event->y()/mLineHeight; + row = point.y()/mLineHeight; if ((col < 16) && (row < ((data.size()+16)/16))) { - mSelectedRow = row; - mSelectedCol = col; + selrow = row; + selcol = col; } else goto _exit; @@ -62,26 +57,163 @@ void DumpView::mousePressEvent(QMouseEvent *event) if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16))) goto _exit; - qDebug("dumpview::selection(%d, %d)", mSelectedRow, mSelectedCol); - update(); - return; + qDebug("dumpview::selection(%d, %d)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} _exit: // Clear existing selection - mSelectedRow = -1; + selrow = -1; +#endif + + return QModelIndex(); +} + +void DumpView::scrollTo( const QModelIndex &index, ScrollHint hint ) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect( const QModelIndex &index ) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden( const QModelIndex &index ) const +{ + return false; +} + +QModelIndex DumpView::moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection( const QRect &rect, + QItemSelectionModel::SelectionFlags flags ) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( const QItemSelection &selection ) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ) +{ + // FIXME(HI) update(); } +void DumpView::selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // TODO(LOW): Assumption - only single selection - enforce/ensure this + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + { + // Non Leaf + + // A non-leaf has no dump data of its own but is rather a + // container for its children. So we calculate ofs/size based + // on this fact + if (selectionModel()->isSelected(index)) + { + selOfs = dump.size(); + populateDump(dump, selOfs, selSize, index); + selSize = dump.size() - selOfs; + } + else + { + populateDump(dump, selOfs, selSize, index); + } + } + else + { + // Leaf + if (selectionModel()->isSelected(index)) + { + int size, j; + + selOfs = dump.size(); + size = model()->data(index, Qt::UserRole).toByteArray().size(); + + // Take care of multiple indexes (2 or more) mapping onto + // same dump byte(s) + j = i-1; + while ((size == 0) && (j >= 0)) + { + size = model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + selOfs -= size; + j++; + } + selSize = size; + } + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + } + // FIXME: Use RawValueRole instead of UserRole + } +} + +// TODO(LOW): rewrite this function - it's a mess! void DumpView::paintEvent(QPaintEvent* event) { - QStylePainter painter(this); + QStylePainter painter(viewport()); QRect offsetRect = mOffsetPaneTopRect; QRect dumpRect = mDumpPaneTopRect; QRect asciiRect = mAsciiPaneTopRect; QPalette pal = palette(); - QByteArray ba; + QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; - //qDebug("dumpview::paintEvent"); + qDebug("dumpview::paintEvent"); // FIXME(LOW): unable to set the self widget's font in constructor painter.setFont(QFont("Courier")); @@ -89,12 +221,15 @@ void DumpView::paintEvent(QPaintEvent* event) // set a white background painter.fillRect(rect(), QBrush(QColor(Qt::white))); + if (model()) + populateDump(data, selOfs, selSize); + // 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); + //ba = data.mid(i, 16); // display offset painter.drawItemText(offsetRect, Qt::AlignLeft | Qt::AlignTop, pal, @@ -128,48 +263,102 @@ void DumpView::paintEvent(QPaintEvent* event) painter.drawItemText(asciiRect, Qt::AlignLeft | Qt::AlignTop, pal, true, asciiStr, QPalette::WindowText); - // overpaint selection (if any) - if ((i/16) == mSelectedRow) + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) { 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()); + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); - if (isPrintable(c)) - selectedAsciiStr.append(QChar(c)); - else - selectedAsciiStr.append(QChar('.')); + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } // display dump r = dumpRect; - if (mSelectedCol < 8) - r.translate(mCharWidth*(mSelectedCol*3), 0); + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); else - r.translate(mCharWidth*(mSelectedCol*3+1), 0); - r.setWidth(mCharWidth*2); + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + 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); + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); else - r.translate(mCharWidth*(mSelectedCol+1), 0); - r.setWidth(mCharWidth); + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + painter.fillRect(r, pal.highlight()); painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, true, selectedAsciiStr, QPalette::HighlightedText); } +_next: // 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 index 4578a91..cdbcde7 100644 --- a/client/dumpview.h +++ b/client/dumpview.h @@ -1,25 +1,33 @@ #include // FIXME: High -class DumpView: public QWidget // QAbstractItemView // FIXME + +class DumpView: public QAbstractItemView { -public: +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; + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; protected: - void mousePressEvent(QMouseEvent *event); - //void mouseMoveEvent(QMouseEvent *event); + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); void paintEvent(QPaintEvent *event); private: - QString toAscii(QByteArray ba); + void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); bool inline isPrintable(char c) {if ((c > 48) && (c < 126)) return true; else return false; } @@ -27,8 +35,8 @@ private: QRect mOffsetPaneTopRect; QRect mDumpPaneTopRect; QRect mAsciiPaneTopRect; - QByteArray data; int mSelectedRow, mSelectedCol; int mLineHeight; int mCharWidth; }; + diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 5800413..a1d4b7f 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -4,22 +4,6 @@ PacketModel::PacketModel(Stream *pStream, QObject *parent) { mpStream = pStream; -#ifdef NEW_IMPL -// Nothing else -#else - populatePacketProtocols(); - - registerFrameTypeProto(); - registerVlanProto(); - registerIpProto(); - registerArpProto(); - registerTcpProto(); - registerUdpProto(); - registerIcmpProto(); - registerIgmpProto(); - registerData(); - registerInvalidProto(); -#endif } int PacketModel::rowCount(const QModelIndex &parent) const @@ -29,22 +13,14 @@ int PacketModel::rowCount(const QModelIndex &parent) const // Parent == Invalid i.e. Invisible Root. // ==> Children are Protocol (Top Level) Items if (!parent.isValid()) -#ifdef NEW_IMPL return mpStream->numProtocols(); -#else - return protoCount(); -#endif // Parent - Valid Item parentId.w = parent.internalId(); switch(parentId.ws.type) { case ITYP_PROTOCOL: -#ifdef NEW_IMPL return mpStream->protocol(parentId.ws.protocol)->numFields(); -#else - return fieldCount(parentId.ws.protocol); -#endif case ITYP_FIELD: return 0; default: @@ -137,40 +113,44 @@ _exit: QVariant PacketModel::data(const QModelIndex &index, int role) const { IndexId id; -#ifdef NEW_IMPL -// Nothing -#else - ProtocolInfo proto; -#endif if (!index.isValid()) return QVariant(); + id.w = index.internalId(); + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QByteArray(); + + case ITYP_FIELD: + return mpStream->protocol(id.ws.protocol)->fieldRawValue( + index.row()); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + if (role != Qt::DisplayRole) return QVariant(); - id.w = index.internalId(); switch(id.ws.type) { case ITYP_PROTOCOL: -#ifdef NEW_IMPL return QString("%1 (%2)") .arg(mpStream->protocol(id.ws.protocol)->protocolShortName()) .arg(mpStream->protocol(id.ws.protocol)->protocolName()); -#else - return protoName(id.ws.protocol); -#endif case ITYP_FIELD: -#ifdef NEW_IMPL return mpStream->protocol(id.ws.protocol)->fieldName(index.row()) + QString(" : ") + mpStream->protocol(id.ws.protocol)->fieldTextValue(index.row()); -#else - return fieldName(id.ws.protocol, index.row()) + - QString(" : ") + - fieldTextValue(id.ws.protocol, index.row()).toString(); -#endif default: qWarning("%s: Unhandled ItemType", __FUNCTION__); @@ -180,759 +160,3 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const return QVariant(); } - -#ifdef NEW_IMPL -// required methods all part of the Stream class -#else -/* -** --------------- Private Stuff ----------------- -** FIXME(MED): Move these to the Stream Class -** -*/ - -/*! - Looking at the stream's protocols and populate an ordered list of - protocols accordingly. The order of protocols will be in the order of - protocol headers viz. - - - None/Eth2/802.3 (Mac Addr) - - LLC - - SNAP - - SVLAN - - CVLAN - - L3 (IP/ARP) - - L4 (TCP/UDP/ICMP/IGMP) - -*/ -void PacketModel::populatePacketProtocols() -{ - int proto; - - // Clear the protocols list - mPacketProtocols.clear(); - - // Check and populate L2 Protocol - switch(mpStream->frameType()) - { - case Stream::e_ft_none: - proto = PTYP_L2_NONE; - break; - - case Stream::e_ft_eth_2: - proto = PTYP_L2_ETH_2; - break; - - case Stream::e_ft_802_3_raw: - proto = PTYP_L2_802_3_RAW; - break; - - case Stream::e_ft_802_3_llc: - mPacketProtocols.append(PTYP_L2_NONE); - proto = PTYP_L2_802_3_LLC; - break; - - case Stream::e_ft_snap: - mPacketProtocols.append(PTYP_L2_NONE); - mPacketProtocols.append(PTYP_L2_802_3_LLC); - proto = PTYP_L2_SNAP; - break; - - default: - qDebug("%s: Unsupported frametype %d", __FUNCTION__, - mpStream->frameType()); - proto = PTYP_INVALID; - } - mPacketProtocols.append(proto); - - // Check and populate VLANs, if present - if (mpStream->vlan()->vlanFlags().testFlag(VlanProtocol::VlanSvlanTagged)) - mPacketProtocols.append(PTYP_SVLAN); - - if (mpStream->vlan()->vlanFlags().testFlag(VlanProtocol::VlanCvlanTagged)) - mPacketProtocols.append(PTYP_CVLAN); - - // Check and populate L3 protocols - switch (mpStream->l3Proto()) - { - case Stream::e_l3_none : - goto _data; - break; - - case Stream::e_l3_ip : - proto = PTYP_L3_IP; - break; - - case Stream::e_l3_arp: - proto = PTYP_L3_ARP; - break; - - default: - qDebug("%s: Unsupported L3 Proto %d", __FUNCTION__, - mpStream->l3Proto()); - proto = PTYP_INVALID; - } - mPacketProtocols.append(proto); - - // Check and populate L4 protocol - switch(mpStream->l4Proto()) - { - case Stream::e_l4_none: - goto _data; - break; - case Stream::e_l4_tcp: - proto = PTYP_L4_TCP; - break; - case Stream::e_l4_udp: - proto = PTYP_L4_UDP; - break; - case Stream::e_l4_icmp: - proto = PTYP_L4_ICMP; - break; - case Stream::e_l4_igmp: - proto = PTYP_L4_IGMP; - break; - default: - qDebug("%s: Unsupported L4 Proto %d", __FUNCTION__, - mpStream->l4Proto()); - proto = PTYP_INVALID; - }; - mPacketProtocols.append(proto); - -_data: - mPacketProtocols.append(PTYP_DATA); -} -/*! - Returns the count of protocols in the current stream -*/ -int PacketModel::protoCount() const -{ - return mPacketProtocols.count(); -} - -/*! - Returns the count of fields in the given protocol -*/ -int PacketModel::fieldCount(int protocol) const -{ - ProtocolInfo proto; - - if (protocol >= mPacketProtocols.count()) - return 0; - - foreach(proto, mProtocols) - { - if (proto.handle == mPacketProtocols.at(protocol)) - { - qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); - qDebug("fieldcount = %d", proto.fieldList.size()); - return proto.fieldList.count(); - } - } - - return 0; -} - -QString PacketModel::protoName(int protocol) const -{ - ProtocolInfo proto; - - if (protocol >= mPacketProtocols.count()) - return 0; - - foreach(proto, mProtocols) - { - if (proto.handle == mPacketProtocols.at(protocol)) - { - qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); - qDebug("fieldcount = %d", proto.fieldList.size()); - return proto.name; - } - } - - return QString(); -} - -QString PacketModel::fieldName(int protocol, int field) const -{ - ProtocolInfo proto; - - if (protocol >= mPacketProtocols.count()) - return 0; - - foreach(proto, mProtocols) - { - if (proto.handle == mPacketProtocols.at(protocol)) - { - qDebug("proto=%d, name=%s",protocol,proto.name.toAscii().data()); - qDebug("fieldcount = %d", proto.fieldList.size()); - if (field >= proto.fieldList.count()) - return QString(); - - return proto.fieldList.at(field).name; - } - } - - return QString(); -} - -QVariant PacketModel::fieldTextValue(int protocol, int field) const -{ - if (protocol >= mPacketProtocols.count()) - return QVariant(); - - switch(mPacketProtocols.at(protocol)) - { - case PTYP_L2_NONE: - case PTYP_L2_ETH_2: - return ethField(field, FROL_TEXT_VALUE); - case PTYP_L2_802_3_RAW: - //return dot3Field(field, FROL_TEXT_VALUE); // FIXME(HIGH) - return ethField(field, FROL_TEXT_VALUE); - case PTYP_L2_802_3_LLC: - return llcField(field, FROL_TEXT_VALUE); - case PTYP_L2_SNAP: - return snapField(field, FROL_TEXT_VALUE); - - case PTYP_SVLAN: - return svlanField(field, FROL_TEXT_VALUE); - case PTYP_CVLAN: - // return cvlanField(field, FROL_TEXT_VALUE); // FIXME(HIGH) - return svlanField(field, FROL_TEXT_VALUE); - - case PTYP_L3_IP: - return ipField(field, FROL_TEXT_VALUE); - case PTYP_L3_ARP: - return QString(); // FIXME(HIGH) - - case PTYP_L4_TCP: - return tcpField(field, FROL_TEXT_VALUE); - case PTYP_L4_UDP: - return udpField(field, FROL_TEXT_VALUE); - case PTYP_L4_ICMP: - return QString(); // FIXME(HIGH) - case PTYP_L4_IGMP: - return QString(); // FIXME(HIGH) - - case PTYP_INVALID: - return QString(); // FIXME(HIGH) - case PTYP_DATA: - return QString(); // FIXME(HIGH) - } - - return QString(); -} - -QVariant PacketModel::ethField(int field, int role) const -{ - FieldInfo info; - - // FIXME(MED): Mac Addr formatting - switch(field) - { - case 0: - info.name = QString("Destination Mac Address"); - info.textValue = QString("%1"). - arg(mpStream->mac()->dstMac(), 12, BASE_HEX, QChar('0')); - break; - case 1: - info.name = QString("Source Mac Address"); - info.textValue = QString("%1"). - arg(mpStream->mac()->srcMac(), 12, BASE_HEX, QChar('0')); - break; - case 2: - info.name = QString("Type"); - info.textValue = QString("0x%1"). - arg(mpStream->eth2()->type(), 4, BASE_HEX, QChar('0')); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - -QVariant PacketModel::llcField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("DSAP"); - info.textValue = QString("0x%1"). - arg(mpStream->llc()->dsap(), 2, BASE_HEX, QChar('0')); - break; - case 1: - info.name = QString("SSAP"); - info.textValue = QString("0x%1"). - arg(mpStream->llc()->ssap(), 2, BASE_HEX, QChar('0')); - break; - case 2: - info.name = QString("Control"); - info.textValue = QString("0x%1"). - arg(mpStream->llc()->ctl(), 2, BASE_HEX, QChar('0')); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - -QVariant PacketModel::snapField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("OUI"); - info.textValue = QString("0x%1"). - arg(mpStream->snap()->oui(), 6, BASE_HEX, QChar('0')); - break; - case 1: - info.name = QString("Type"); - info.textValue = QString("0x%1"). - arg(mpStream->eth2()->type(), 4, BASE_HEX, QChar('0')); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - -QVariant PacketModel::svlanField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("TPID"); - info.textValue = QString("0x%1"). - arg(mpStream->vlan()->stpid(), 4, BASE_HEX, QChar('0')); - break; - case 1: - info.name = QString("PCP"); - info.textValue = QString("%1"). - arg(mpStream->vlan()->svlanPrio()); - break; - case 2: - info.name = QString("DE"); - info.textValue = QString("%1"). - arg(mpStream->vlan()->svlanCfi()); - break; - case 3: - info.name = QString("VlanId"); - info.textValue = QString("%1"). - arg(mpStream->vlan()->svlanId()); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - - -QVariant PacketModel::ipField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("Version"); - info.textValue = QString("%1"). - arg(mpStream->ip()->ver()); - break; - case 1: - info.name = QString("Header Length"); - info.textValue = QString("%1"). - arg(mpStream->ip()->hdrLen()); - break; - case 2: - info.name = QString("TOS/DSCP"); - info.textValue = QString("0x%1"). - arg(mpStream->ip()->tos(), 2, BASE_HEX, QChar('0')); - break; - case 3: - info.name = QString("Total Length"); - info.textValue = QString("%1"). - arg(mpStream->ip()->totLen()); - break; - case 4: - info.name = QString("ID"); - info.textValue = QString("0x%1"). - arg(mpStream->ip()->id(), 2, BASE_HEX, QChar('0')); - break; - case 5: - info.name = QString("Flags"); - info.textValue = QString("0x%1"). - arg(mpStream->ip()->flags(), 2, BASE_HEX, QChar('0')); // FIXME(HIGH) - break; - case 6: - info.name = QString("Fragment Offset"); - info.textValue = QString("%1"). - arg(mpStream->ip()->fragOfs()); - break; - case 7: - info.name = QString("TTL"); - info.textValue = QString("%1"). - arg(mpStream->ip()->ttl()); - break; - case 8: - info.name = QString("Protocol Type"); - info.textValue = QString("0x%1"). - arg(mpStream->ip()->proto(), 2, BASE_HEX, QChar('0')); - break; - case 9: - info.name = QString("Checksum"); - info.textValue = QString("0x%1"). - arg(mpStream->ip()->cksum(), 4, BASE_HEX, QChar('0')); - break; - case 10: - info.name = QString("Source IP"); - info.textValue = QHostAddress(mpStream->ip()->srcIp()).toString(); - break; - case 11: - info.name = QString("Destination IP"); - info.textValue = QHostAddress(mpStream->ip()->dstIp()).toString(); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - - -QVariant PacketModel::tcpField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("Source Port"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->srcPort()); - break; - case 1: - info.name = QString("Destination Port"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->dstPort()); - break; - case 2: - info.name = QString("Seq Number"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->seqNum()); - break; - case 3: - info.name = QString("Ack Number"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->ackNum()); - break; - case 4: - info.name = QString("Header Length"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->hdrLen()); - break; - case 5: - info.name = QString("Reserved"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->rsvd()); - break; - case 6: - info.name = QString("Flags"); - info.textValue = QString("0x%1"). - arg(mpStream->tcp()->flags(), 2, BASE_HEX, QChar('0')); - break; - case 7: - info.name = QString("Window"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->flags()); - break; - case 8: - info.name = QString("Checksum"); - info.textValue = QString("0x%1"). - arg(mpStream->tcp()->cksum(), 4, BASE_HEX, QChar('0')); - break; - case 9: - info.name = QString("Urgent Pointer"); - info.textValue = QString("%1"). - arg(mpStream->tcp()->urgPtr()); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - - -QVariant PacketModel::udpField(int field, int role) const -{ - FieldInfo info; - - switch(field) - { - case 0: - info.name = QString("Source Port"); - info.textValue = QString("%1"). - arg(mpStream->udp()->srcPort()); - break; - case 1: - info.name = QString("Destination Port"); - info.textValue = QString("%1"). - arg(mpStream->udp()->dstPort()); - break; - case 2: - info.name = QString("Total Length"); - info.textValue = QString("%1"). - arg(mpStream->udp()->totLen()); - break; - case 3: - info.name = QString("Checksum"); - info.textValue = QString("0x%1"). - arg(mpStream->udp()->cksum(), 4, BASE_HEX, QChar('0')); - break; - default: - info.name = QString(); - info.textValue = QString(); - } - - switch(role) - { - case FROL_NAME: - return info.name; - case FROL_TEXT_VALUE: - return info.textValue; - default: - ; - } - - Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); -} - - - -/* -** ------------- Registration Functions --------------- -*/ - -void PacketModel::registerProto(uint handle, char *name, char *abbr) -{ - ProtocolInfo proto; - - proto.handle = handle; - proto.name = QString(name); - proto.abbr = QString(abbr); - mProtocols.append(proto); -} - -void PacketModel::registerField(uint protoHandle, char *name, char *abbr) -{ - for (int i = 0; i < mProtocols.size(); i++) - { - if (mProtocols.at(i).handle == protoHandle) - { - FieldInfo field; - - field.name = QString(name); - field.abbr = QString(abbr); - mProtocols[i].fieldList.append(field); - qDebug("proto = %d, name = %s", protoHandle, name); - break; - } - } -} - -void PacketModel::registerFrameTypeProto() -{ - registerProto(PTYP_L2_NONE, "None", ""); - registerField(PTYP_L2_NONE, "Destination Mac", "dstMac"); - registerField(PTYP_L2_NONE, "Source Mac", "srcMac"); - - registerProto(PTYP_L2_ETH_2, "Ethernet II", "eth"); - registerField(PTYP_L2_ETH_2, "Destination Mac", "dstMac"); - registerField(PTYP_L2_ETH_2, "Source Mac", "srcMac"); - registerField(PTYP_L2_ETH_2, "Ethernet Type", "type"); - - registerProto(PTYP_L2_802_3_RAW, "IEEE 802.3 Raw", "dot3raw"); - registerField(PTYP_L2_802_3_RAW, "Destination Mac", "dstMac"); - registerField(PTYP_L2_802_3_RAW, "Source Mac", "srcMac"); - registerField(PTYP_L2_802_3_RAW, "Length", "len"); - - registerProto(PTYP_L2_802_3_LLC, "802.3 LLC", "dot3llc"); - registerField(PTYP_L2_802_3_LLC, "Destination Service Acces Point", "dsap"); - registerField(PTYP_L2_802_3_LLC, "Source Service Acces Point", "ssap"); - registerField(PTYP_L2_802_3_LLC, "Control", "ctl"); - - registerProto(PTYP_L2_SNAP, "SNAP", "dot3snap"); - registerField(PTYP_L2_SNAP, "Organisationally Unique Identifier", "oui"); - registerField(PTYP_L2_SNAP, "Type", "type"); - -} - -void PacketModel::registerVlanProto() -{ - registerProto(PTYP_SVLAN, "IEEE 802.1ad Service VLAN", "SVLAN"); - - registerField(PTYP_SVLAN, "Tag Protocol Identifier", "tpid"); - registerField(PTYP_SVLAN, "Priority Code Point", "pcp"); - registerField(PTYP_SVLAN, "Drop Eligible", "de"); - registerField(PTYP_SVLAN, "VLAN Identifier", "vlan"); - - registerProto(PTYP_CVLAN, "IEEE 802.1Q VLAN/CVLAN", "VLAN"); - - registerField(PTYP_CVLAN, "Tag Protocol Identifier", "tpid"); - registerField(PTYP_CVLAN, "Priority", "prio"); - registerField(PTYP_CVLAN, "Canonical Format Indicator", "cfi"); - registerField(PTYP_CVLAN, "VLAN Identifier", "vlan"); -} - -void PacketModel::registerIpProto() -{ - registerProto(PTYP_L3_IP, "Internet Protocol version 4", "IPv4"); - - registerField(PTYP_L3_IP, "Version", "ver"); - registerField(PTYP_L3_IP, "Header Length", "hdrlen"); - registerField(PTYP_L3_IP, "Type of Service/DiffServ Code Point", "tos"); - registerField(PTYP_L3_IP, "Total Length", "len"); - registerField(PTYP_L3_IP, "Identification", "id"); - registerField(PTYP_L3_IP, "Flags", "flags"); - registerField(PTYP_L3_IP, "Fragment Offset", "fragofs"); - registerField(PTYP_L3_IP, "Time to Live", "ttl"); - registerField(PTYP_L3_IP, "Protocol Id", "proto"); - registerField(PTYP_L3_IP, "Checksum", "cksum"); - registerField(PTYP_L3_IP, "Source IP", "srcip"); - registerField(PTYP_L3_IP, "Destination IP", "dstip"); -} - -void PacketModel::registerArpProto() -{ - // TODO (LOW) -} - -void PacketModel::registerTcpProto() -{ - registerProto(PTYP_L4_TCP, "Transmission Control Protocol", "TCP"); - - registerField(PTYP_L4_TCP, "Source Port", "srcport"); - registerField(PTYP_L4_TCP, "Destination Port", "dstport"); - registerField(PTYP_L4_TCP, "Sequence Number", "seqnum"); - registerField(PTYP_L4_TCP, "Acknowledgement Number", "acknum"); - registerField(PTYP_L4_TCP, "Header Length", "hdrlen"); - registerField(PTYP_L4_TCP, "Reserved", "rsvd"); - registerField(PTYP_L4_TCP, "Flags", "flags"); - registerField(PTYP_L4_TCP, "Window", "win"); - registerField(PTYP_L4_TCP, "Checksum", "cksum"); - registerField(PTYP_L4_TCP, "Urgent Pointer", "urgptr"); -} - -void PacketModel::registerUdpProto() -{ - registerProto(PTYP_L4_UDP, "User Datagram Protocol", "UDP"); - - registerField(PTYP_L4_UDP, "Source Port", "srcport"); - registerField(PTYP_L4_UDP, "Destination Port", "dstport"); - registerField(PTYP_L4_UDP, "Length", "len"); - registerField(PTYP_L4_UDP, "Checksum", "cksum"); -} - -void PacketModel::registerIcmpProto() -{ - // TODO (LOW) -} - -void PacketModel::registerIgmpProto() -{ - // TODO (LOW) -} - -void PacketModel::registerInvalidProto() -{ - registerProto(PTYP_INVALID, "Invalid Protocol (bug in code)", "invalid"); -} - -void PacketModel::registerData() -{ - registerProto(PTYP_DATA, "Data", "data"); -} - -#endif diff --git a/client/stream.cpp b/client/stream.cpp index 30568e5..653859c 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -1,3 +1,4 @@ +#include #include #include "stream.h" @@ -6,6 +7,72 @@ #define BASE_HEX 16 +QString PayloadProtocol::fieldTextValue(int index) +{ + int len; + quint32 pat; + QString textValue; + + if (parentStream) + { + len = parentStream->frameLen() - parentStream->protocolHeaderSize(); + pat = parentStream->pattern(); + } + else + { + len = 1500; // FIXME(HI): testing only + pat = 0x0a0b0c0d; + } + + // Make a larger string and then resize to the correct size to + // take care of the case where len is not a multiple of pattern size + if (len > 0) + { + // TODO(LOW): allow non-4byte patterns!!! + int w = 4; // data pattern size + + for (int i = 0; i < (len/w + 1); i++) + textValue.append(QString("%1").arg( + pat, w*2, BASE_HEX, QChar('0'))); + textValue.resize(len); + } + + return textValue; +} + +QByteArray PayloadProtocol::fieldRawValue(int index) +{ + int len; + quint32 pat; + QByteArray rawValue; + + if (parentStream) + { + len = parentStream->frameLen() - parentStream->protocolHeaderSize(); + pat = parentStream->pattern(); + } + else + { + len = 1500; // FIXME(HI): testing only + pat = 0x0a0b0c0d; + } + + // Make a larger byteArray and then resize to the correct size to + // take care of the case where len is not a multiple of pattern size + if (len > 0) + { + // TODO(LOW): allow non-4byte patterns!!! + int w = 4; // data pattern size + + rawValue.resize(len + 4); + for (int i = 0; i < (len/w + 1); i++) + qToBigEndian(pat, (uchar*) (rawValue.data() + i*sizeof(pat))); + rawValue.resize(len); + } + + return rawValue; +} + QString MacProtocol::fieldName(int index) { QString name; @@ -53,6 +120,24 @@ QByteArray MacProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(8); + qToBigEndian(dstMac(), (uchar *) rawValue.data()); + rawValue.remove(0, 2); + qDebug("dstMac(%d): %s", rawValue.size(), rawValue.toHex().constData()); + break; + case 1: + rawValue.resize(8); + qToBigEndian(srcMac(), (uchar *) rawValue.data()); + rawValue.remove(0, 2); + qDebug("srcMac(%d): %s", rawValue.size(), rawValue.toHex().constData()); + break; + default: + break; + } + return rawValue; } @@ -109,6 +194,24 @@ QByteArray LlcProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(1); + rawValue[0] = dsap(); + break; + case 1: + rawValue.resize(1); + rawValue[0] = ssap(); + break; + case 2: + rawValue.resize(1); + rawValue[0] = ctl(); + break; + default: + break; + } + return rawValue; } @@ -151,6 +254,17 @@ QByteArray SnapProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(4); + qToBigEndian(oui(), (uchar *) rawValue.data()); + rawValue.remove(0, 1); + break; + default: + break; + } + return rawValue; } @@ -191,9 +305,58 @@ QByteArray Eth2Protocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(2); + qToBigEndian(type(), (uchar*) rawValue.data()); + break; + default: + break; + } + return rawValue; } +QString Dot3Protocol::fieldName(int index) +{ + QString name; + + switch(index) + { + case 0: + name = QString("Length"); + break; + default: + name = QString(); + break; + } + return name; +} + +QString Dot3Protocol::fieldTextValue(int index) +{ + if (parentStream) + return QString("%1").arg(parentStream->frameLen()); + else + return QString("00"); +} + +QByteArray Dot3Protocol::fieldRawValue(int index) +{ + QByteArray ba; + + if (parentStream) + qToBigEndian((quint16) parentStream->frameLen(), (uchar*) ba.data()); + else + { + ba.resize(2); + ba[0] = ba[1] = 0; + } + + return ba; +} + int VlanProtocol::numFields() { if (isSingleTagged()) @@ -323,6 +486,56 @@ QByteArray VlanProtocol::fieldRawValue(int index) { QByteArray rawValue; + if (isDoubleTagged()) + { + switch(index) + { + case 0: + rawValue.resize(2); + qToBigEndian(stpid(), (uchar*) rawValue.data()); + break; + case 1: + rawValue.resize(2); + qToBigEndian((svlanPrio() << 13) | (svlanCfi() < 12) | svlanId(), + (uchar*) rawValue.data()); + break; + case 2: + // Combined with prio above + break; + case 3: + // Combined with prio above + break; + default: + index -= 4; + goto _single_tag; + } + + goto _exit; + } + +_single_tag: + switch(index) + { + case 0: + rawValue.resize(2); + qToBigEndian(ctpid(), (uchar*) rawValue.data()); + break; + case 1: + rawValue.resize(2); + qToBigEndian((cvlanPrio() << 13) | (cvlanCfi() < 12) | cvlanId(), + (uchar*) rawValue.data()); + break; + case 2: + // Combined with prio above + break; + case 3: + // Combined with prio above + break; + default: + break; + } + +_exit: return rawValue; } @@ -438,6 +651,60 @@ QByteArray IpProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(1); + //qToBigEndian((ver() << 4) | hdrLen(), (uchar*) rawValue.data()); + rawValue[0]=(ver() << 4) | hdrLen(); + break; + case 1: + // Combined with previous 4 bits of ver!! + break; + case 2: + rawValue.resize(1); + qToBigEndian(tos(), (uchar*) rawValue.data()); + break; + case 3: + rawValue.resize(2); + qToBigEndian(totLen(), (uchar*) rawValue.data()); + break; + case 4: + rawValue.resize(2); + qToBigEndian(id(), (uchar*) rawValue.data()); + break; + case 5: + rawValue.resize(2); + qToBigEndian((quint16)((flags() << 13) | fragOfs()), + (uchar*) rawValue.data()); + break; + case 6: + // Combined with previous 3 bits of flags!! + break; + case 7: + rawValue.resize(1); + qToBigEndian(ttl(), (uchar*) rawValue.data()); + break; + case 8: + rawValue.resize(1); + qToBigEndian(proto(), (uchar*) rawValue.data()); + break; + case 9: + rawValue.resize(2); + qToBigEndian(cksum(), (uchar*) rawValue.data()); + break; + case 10: + rawValue.resize(4); + qToBigEndian(srcIp(), (uchar*) rawValue.data()); + break; + case 11: + rawValue.resize(4); + qToBigEndian(dstIp(), (uchar*) rawValue.data()); + break; + default: + break; + } + return rawValue; } @@ -520,7 +787,7 @@ QString TcpProtocol::fieldTextValue(int index) break; case 7: textValue = QString("%1"). - arg(flags()); + arg(window(), 2, BASE_HEX, QChar('0')); break; case 8: textValue = QString("0x%1"). @@ -541,6 +808,51 @@ QByteArray TcpProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(2); + qToBigEndian(srcPort(), (uchar*) rawValue.data()); + break; + case 1: + rawValue.resize(2); + qToBigEndian(dstPort(), (uchar*) rawValue.data()); + break; + case 2: + rawValue.resize(4); + qToBigEndian(seqNum(), (uchar*) rawValue.data()); + break; + case 3: + rawValue.resize(4); + qToBigEndian(ackNum(), (uchar*) rawValue.data()); + break; + case 4: + rawValue.resize(1); + rawValue[0] = (hdrLen() << 4) | rsvd(); + break; + case 5: + // Combined with hdrLen above + break; + case 6: + rawValue.resize(1); + rawValue[0] = flags(); + break; + case 7: + rawValue.resize(2); + qToBigEndian(window(), (uchar*) rawValue.data()); + break; + case 8: + rawValue.resize(2); + qToBigEndian(cksum(), (uchar*) rawValue.data()); + break; + case 9: + rawValue.resize(2); + qToBigEndian(urgPtr(), (uchar*) rawValue.data()); + break; + default: + break; + } + return rawValue; } @@ -602,6 +914,28 @@ QByteArray UdpProtocol::fieldRawValue(int index) { QByteArray rawValue; + switch(index) + { + case 0: + rawValue.resize(2); + qToBigEndian(srcPort(), (uchar*) rawValue.data()); + break; + case 1: + rawValue.resize(2); + qToBigEndian(dstPort(), (uchar*) rawValue.data()); + break; + case 2: + rawValue.resize(2); + qToBigEndian(totLen(), (uchar*) rawValue.data()); + break; + case 3: + rawValue.resize(2); + qToBigEndian(cksum(), (uchar*) rawValue.data()); + break; + default: + break; + } + return rawValue; } @@ -622,12 +956,14 @@ Stream::Stream() // mCore->set_stream_id(mId); mUnknown = new UnknownProtocol; + mPayload = new PayloadProtocol(); //FIXME(MED): need to pass parent stream mMac = new MacProtocol; mLlc = new LlcProtocol; mSnap = new SnapProtocol; mEth2 = new Eth2Protocol; + mDot3 = new Dot3Protocol(); // FIXME(MED): need to pass parent stream mVlan = new VlanProtocol; mIp = new IpProtocol; @@ -776,9 +1112,23 @@ void Stream::updateSelectedProtocols() selectedProtocols.append(proto); _data: + + mProtocolHeaderSize = 0; +#ifndef SRIVATSP +#if 0 + for (int i = 0; i < selectedProtocols.size(); i++) + mProtocolHeaderSize += protocol(i)->protocolRawValue().size(); +#endif +#endif selectedProtocols.append(PTYP_DATA); } +int Stream::protocolHeaderSize() +{ + updateSelectedProtocols(); // FIXME(HI): shd not happen everytime + return mProtocolHeaderSize; +} + int Stream::numProtocols() { updateSelectedProtocols(); // FIXME(HI): shd not happen everytime @@ -815,7 +1165,7 @@ AbstractProtocol* Stream::protocol(int index) case PTYP_L2_ETH_2: return eth2(); case PTYP_L2_802_3_RAW: - return eth2(); // FIXME(HI): define a dot3 protocol? + return dot3(); case PTYP_L2_802_3_LLC: return llc(); @@ -843,7 +1193,7 @@ AbstractProtocol* Stream::protocol(int index) case PTYP_INVALID: return mUnknown; case PTYP_DATA: - return mUnknown; // FIXME(MED) define a "data" protocol? + return mPayload; default: return mUnknown; } diff --git a/client/stream.h b/client/stream.h index 0bb35cd..9500064 100644 --- a/client/stream.h +++ b/client/stream.h @@ -2,6 +2,7 @@ #define _STREAM_H #include + #include #include #include "../common/protocol.pb.h" @@ -17,6 +18,8 @@ class PacketModel; #define IP_PROTO_UDP 0x11 +class Stream; + class AbstractProtocol { // TODO(LOW) @@ -36,6 +39,16 @@ public: { return QString("AbsProto"); } virtual int numFields() { return 1; } + QByteArray protocolRawValue() + { + QByteArray ba; +#ifndef SRIVATSP +#else + for (int i=0; i < numFields(); i++) + ba.append(fieldRawValue(i)); +#endif + return ba; + } virtual QString fieldName(int index) { return QString("AbstractField"); } virtual QString fieldTextValue(int index) @@ -64,7 +77,31 @@ public: QString fieldTextValue(int index) { return QString("UnknownFieldValue"); } QByteArray fieldRawValue(int index) - { return QByteArray(4, '\0'); } + { return QByteArray(); } +}; + +class PayloadProtocol: public AbstractProtocol +{ + Stream *parentStream; + OstProto::Ack d; // FIXME(HI): move payload related vars from + // stream into here + +public: + PayloadProtocol (Stream *stream = NULL) { parentStream = stream; } + virtual ~PayloadProtocol() {} + + virtual ::google::protobuf::Message& data() { return d; } + + virtual QString protocolName() + { return QString("Payload Data"); } + QString protocolShortName() + { return QString("DATA"); } + int numFields() + { return 1; } + QString fieldName(int index) + { return QString("Data"); } + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); }; class MacProtocol : public AbstractProtocol @@ -238,6 +275,37 @@ public: QByteArray fieldRawValue(int index); }; + +class Dot3Protocol : public AbstractProtocol +{ +private: + Stream *parentStream; + OstProto::Ack d; // FIXME(HI): replace 'ack' with somehting else + +public: + Dot3Protocol(Stream *stream = NULL) + { parentStream = stream; qDebug("parentStream = %p", stream); } + virtual ~Dot3Protocol() {} + virtual ::google::protobuf::Message& data() {return d;} + +#if 0 // FIXME(HI): remove? + quint16 length() + { return d.length(); } + bool setLength(quint16 length) + { return true; } +#endif + + virtual QString protocolName() + { return QString("802.3 Length"); } + QString protocolShortName() + { return QString("LEN"); } + int numFields() + { return 1; } + QString fieldName(int index); + QString fieldTextValue(int index); + QByteArray fieldRawValue(int index); +}; + class VlanProtocol : public AbstractProtocol { OstProto::Vlan d; @@ -765,11 +833,14 @@ class Stream { OstProto::StreamCore *mCore; UnknownProtocol *mUnknown; + PayloadProtocol *mPayload; MacProtocol *mMac; LlcProtocol *mLlc; SnapProtocol *mSnap; Eth2Protocol *mEth2; + Dot3Protocol *mDot3; + VlanProtocol *mVlan; IpProtocol *mIp; @@ -781,11 +852,15 @@ class Stream { IgmpProtocol *mIgmp; public: + //PayloadProtocol* Payload() { return mPayload; } MacProtocol* mac() { return mMac; } + void* core() { return mCore; } // FIXME(HI): Debug ONLY + LlcProtocol* llc() { return mLlc; } SnapProtocol* snap() { return mSnap; } Eth2Protocol* eth2() { return mEth2; } + Dot3Protocol* dot3() { return mDot3; } VlanProtocol* vlan() { return mVlan; } IpProtocol* ip() { return mIp; } @@ -954,15 +1029,19 @@ public: //--------------------------------------------------------------- // Methods for use by Packet Model //--------------------------------------------------------------- - QList selectedProtocols; int numProtocols(); + + //! Includes ALL protocol headers excluding payload data + int protocolHeaderSize(); #if 0 int protocolId(int index); int protocolIndex(int id); #endif AbstractProtocol* protocol(int index); private: + QList selectedProtocols; + int mProtocolHeaderSize; void updateSelectedProtocols(); }; diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index a881dce..0202825 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -16,13 +16,13 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, //mpStreamList = streamList; mCurrentStreamIndex = streamIndex; LoadCurrentStream(); - mpPacketModel = new PacketModel(&mPort.streamByIndex(mCurrentStreamIndex), this); tvPacketTree->setModel(mpPacketModel); mpPacketModelTester = new ModelTest(mpPacketModel); tvPacketTree->header()->hide(); - + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); // FIXME(MED): Enable this navigation pbPrev->setDisabled(true); @@ -625,6 +625,11 @@ void StreamConfigDialog::StoreCurrentStream() { pStream->mac()->setDstMac( leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); +#if 1 + qDebug("%s: dstMac = %llx [%s] %d", __FUNCTION__, + pStream->mac()->dstMac(), + leDstMac->text().toAscii().constData(), isOk); +#endif pStream->mac()->setDstMacMode( (MacProtocol::MacAddrMode) cmbDstMacMode->currentIndex()); pStream->mac()->setDstMacCount( @@ -634,6 +639,9 @@ void StreamConfigDialog::StoreCurrentStream() pStream->mac()->setSrcMac( leSrcMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); + qDebug("%s: srcMac = %llx [%s] %d", __FUNCTION__, + pStream->mac()->srcMac(), + leSrcMac->text().toAscii().constData(), isOk); pStream->mac()->setSrcMacMode( (MacProtocol::MacAddrMode) cmbSrcMacMode->currentIndex()); pStream->mac()->setSrcMacCount( diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index b568d52..503eb27 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -33,7 +33,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 0 + 2 @@ -2036,6 +2036,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff QAbstractItemView::SelectItems + + true + diff --git a/common/protocol.proto b/common/protocol.proto index e620b2d..9cb835f 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -190,8 +190,8 @@ message StreamCore { optional uint32 data_start_ofs = 13; // Frame Length (includes CRC) - optional FrameLengthMode len_mode = 14; - optional uint32 frame_len = 15; + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; optional uint32 frame_len_min = 16; optional uint32 frame_len_max = 17; From 4cf80d4ee413dceebb28c8424b17a59012b614b0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 14 Sep 2008 12:03:53 +0000 Subject: [PATCH 009/294] Demo code for stats being checked in --- client/port.cpp | 10 +- client/port.h | 10 +- client/portgroup.cpp | 78 +++++++ client/portgroup.h | 10 + client/portgrouplist.cpp | 3 + client/portgrouplist.h | 4 + client/portstatsmodel.cpp | 117 ++++++++-- client/portstatsmodel.h | 19 +- client/portstatswindow.cpp | 53 ++++- client/portstatswindow.h | 14 +- common/protocol.proto | 18 +- server/drone.pro | 2 +- server/myservice.cpp | 435 ++++++++++++++++++++++++++++++++++--- server/myservice.h | 59 ++++- 14 files changed, 747 insertions(+), 85 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 89d94c3..06f40ae 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -16,6 +16,7 @@ Port::Port(quint32 id, quint32 portGroupId) { mPortId = id; d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); mPortGroupId = portGroupId; } @@ -166,10 +167,6 @@ void Port::getModifiedStreamsSinceLastSync( } } - -// -// ----------- SLOTS ------------- -// void Port::when_syncComplete() { qSort(mStreams); @@ -179,3 +176,8 @@ void Port::when_syncComplete() mLastSyncStreamList.append(mStreams[i].id()); } +void Port::updateStats(OstProto::PortStats *portStats) +{ + stats.MergeFrom(*portStats); +} + diff --git a/client/port.h b/client/port.h index f96d31c..b16d680 100644 --- a/client/port.h +++ b/client/port.h @@ -13,8 +13,9 @@ class Port { //friend class StreamModel; private: - static uint mAllocStreamId; - OstProto::Port d; + static uint mAllocStreamId; + OstProto::Port d; + OstProto::PortStats stats; // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' quint32 mPortId; @@ -68,6 +69,7 @@ public: Q_ASSERT(index < mStreams.size()); return mStreams[index]; } + OstProto::PortStats getStats() { return stats; } // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal void updatePortConfig(OstProto::Port *port); @@ -89,7 +91,11 @@ public: void getModifiedStreamsSinceLastSync( OstProto::StreamConfigList &streamConfigList); + void when_syncComplete(); + + void updateStats(OstProto::PortStats *portStats); + }; #endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 3de90cc..70cb66e 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -413,3 +413,81 @@ void PortGroup::processModifyStreamAck(OstProto::Ack *ack) // TODO(HI): Apply Button should now be disabled???!!!!??? } + +void PortGroup::startTx(QList portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("In %s", __FUNCTION__); + + for (int i = 0; i < portList.size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList.at(i)); + } + + serviceStub->startTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, ack)); +} + +void PortGroup::processStartTxAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::getPortStats() +{ + OstProto::PortStatsList *portStatsList = new OstProto::PortStatsList; + + qDebug("In %s", __FUNCTION__); + + serviceStub->getStats(rpcController, &portIdList, portStatsList, + NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); +} + +void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) +{ + qDebug("In %s", __FUNCTION__); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList->port_stats_size(); i++) + { + uint id; + + id = portStatsList->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id].updateStats(portStatsList->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + delete portStatsList; +} + +void PortGroup::clearPortStats() +{ + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("In %s", __FUNCTION__); + + serviceStub->clearStats(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, ack)); +} + +void PortGroup::processClearStatsAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + diff --git a/client/portgroup.h b/client/portgroup.h index 89c02af..e2b5317 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -69,10 +69,20 @@ public: OstProto::StreamConfigList *streamConfigList = NULL); void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList portList); + void processStartTxAck(OstProto::Ack *ack); + + void getPortStats(); + void processPortStatsList(OstProto::PortStatsList *portStatsList); + void clearPortStats(); + void processClearStatsAck(OstProto::Ack *ack); + signals: void portGroupDataChanged(PortGroup* portGroup); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); private slots: void on_rpcChannel_stateChanged(); diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 06df02d..a503449 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -74,6 +74,9 @@ void PortGroupList::addPortGroup(PortGroup &portGroup) connect(&portGroup, SIGNAL(portListChanged(quint32)), &mPortStatsModel, SLOT(when_portListChanged())); + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + mPortGroups.append(&portGroup); portGroup.connectToHost(); diff --git a/client/portgrouplist.h b/client/portgrouplist.h index 84bdb9c..aadd24d 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -28,6 +28,7 @@ class PortGroupList : public QObject { public: PortGroupList::PortGroupList(); + PortModel* getPortModel() { return &mPortGroupListModel; } PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } StreamModel* getStreamModel() { return &mStreamListModel; } @@ -37,6 +38,9 @@ public: PortGroup& portGroup(const QModelIndex& index); Port& port(const QModelIndex& index); + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + void addPortGroup(PortGroup &portGroup); void removePortGroup(PortGroup &portGroup); diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 5da586b..d8b40e6 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -1,10 +1,18 @@ #include "portstatsmodel.h" #include "portgrouplist.h" +#include + PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) : QAbstractTableModel(parent) { + QTimer *timer; + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(5000); } int PortStatsModel::rowCount(const QModelIndex &parent) const @@ -32,9 +40,33 @@ int PortStatsModel::columnCount(const QModelIndex &parent ) const return numPorts.last(); } +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + QVariant PortStatsModel::data(const QModelIndex &index, int role) const { - int pgidx, pidx, portNum; + uint pgidx, pidx; // Check for a valid index if (!index.isValid()) @@ -50,31 +82,44 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const if (index.column() >= (numPorts.last())) return QVariant(); - // TODO(LOW): Optimize using binary search: see qLowerBound() - portNum = index.column() + 1; - for (pgidx = 0; pgidx < numPorts.size(); pgidx++) - if (portNum <= numPorts.at(pgidx)) - break; - - if (pgidx) - { - if (numPorts.at(pgidx -1)) - pidx = (portNum - 1) % numPorts.at(pgidx - 1); - else - pidx = portNum - 1; - } - else - pidx = portNum - 1; - - //qDebug("PSM: %d - %d, %d", index.column(), pgidx, pidx); + getDomainIndexes(index, pgidx, pidx); // Check role if (role == Qt::DisplayRole) { -#if 0 // PB - return pgl->mPortGroups.at(pgidx)->mPorts.at(pidx).mPortStats[index.row()]; -#endif - return 0; //FIXME: Get actual port stats + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx].getStats(); + + switch(index.row()) + { + case e_STAT_FRAMES_RCVD: + return stats.rx_pkts(); + + case e_STAT_FRAMES_SENT: + return stats.tx_pkts(); + + case e_STAT_FRAME_SEND_RATE: + return stats.tx_pps(); + + case e_STAT_FRAME_RECV_RATE: + return stats.rx_pps(); + + case e_STAT_BYTES_RCVD: + return stats.rx_bytes(); + + case e_STAT_BYTES_SENT: + return stats.tx_bytes(); + + case e_STAT_BYTE_SEND_RATE: + return stats.tx_bps(); + + case e_STAT_BYTE_RECV_RATE: + return stats.rx_bps(); + + default: + return 0; + } } else return QVariant(); @@ -92,6 +137,22 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in return PortStatName.at(section); } +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + portList.clear(); + + for (int i = 0; i < indices.size(); i++) + { + //getDomainIndexes(indices.at(i), portGroupIdx, portIdx); + + for (int j = 0; j < portList.size(); j++) + { + // FIXME(HI): Incomplete!!!! + } + } +} + // // Slots // @@ -121,4 +182,16 @@ void PortStatsModel::on_portStatsUpdate(int port, void*stats) emit dataChanged(topLeft, bottomRight); } +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} +void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) +{ + // FIXME(MED): update only the changed ports, not all + reset(); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 6bf5c5a..6dc4693 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -33,9 +33,8 @@ class PortStatsModel : public QAbstractTableModel { Q_OBJECT - PortGroupList *pgl; - public: + PortStatsModel(PortGroupList *p, QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; @@ -44,16 +43,32 @@ class PortStatsModel : public QAbstractTableModel QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + class PortGroupAndPortList { + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + public slots: void when_portListChanged(); void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); private: + PortGroupList *pgl; + // numPorts stores the num of ports per portgroup // in the same order as the portgroups are index in the pgl // Also it stores them as cumulative totals QList numPorts; + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + }; #endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 1339ebb..28b8f88 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -5,20 +5,71 @@ #include "QHeaderView" -//PortStatsWindow::PortStatsWindow(QWidget *parent) : QDialog (parent) PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) { setupUi(this); + this->pgl = pgl; model = pgl->getPortStatsModel(); tvPortStats->setModel(model); tvPortStats->horizontalHeader()->setMovable(true); + tvPortStats->verticalHeader()->resizeSections(QHeaderView::ResizeToContents); } PortStatsWindow::~PortStatsWindow() { } +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + // TODO(MED): get selected ports + + if (pgl->numPortGroups()) + { + QList portIdList; + + // FIXME(HI): Testing only!!! + portIdList.append(1); // MS Loopback adapter + pgl->portGroupByIndex(0).startTx(portIdList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + // TODO(MED) +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) +} + +void PortStatsWindow::on_tbClear_clicked() +{ + // TODO(MED) +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + void PortStatsWindow::on_tbFilter_clicked() { bool ok; diff --git a/client/portstatswindow.h b/client/portstatswindow.h index e5a1a97..98642c1 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -6,17 +6,29 @@ #include "ui_portstatswindow.h" #include "portgrouplist.h" -class PortStatsWindow : public QDialog, public Ui::PortStatsWindow +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow { Q_OBJECT public: PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); ~PortStatsWindow(); + private: + PortGroupList *pgl; QAbstractItemModel *model; private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + void on_tbFilter_clicked(); }; diff --git a/common/protocol.proto b/common/protocol.proto index 9cb835f..7ffd54f 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -34,7 +34,7 @@ message Llc { message Snap { optional uint32 oui = 1; - optional uint32 type = 2; + //optional uint32 type = 2; } message Eth2 { @@ -126,7 +126,7 @@ message Udp { optional uint32 src_port = 3 [default = 8902]; optional uint32 dst_port = 4 [default = 80]; - optional uint32 totLen = 5; + optional uint32 totlen = 5; optional uint32 cksum = 6; } @@ -279,11 +279,21 @@ message CaptureBufferList { } message PortStats { - // TODO + required PortId port_id = 1; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pps = 13; + optional uint64 rx_bps = 14; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pps = 23; + optional uint64 tx_bps = 24; } message PortStatsList { - repeated PortStats list = 1; + repeated PortStats port_stats = 1; } service OstService { diff --git a/server/drone.pro b/server/drone.pro index df79396..79cbd1a 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,7 +1,7 @@ TEMPLATE = app CONFIG += qt QT += network -DEFINES += HAVE_REMOTE +DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "c:\msys\1.0\local\include" INCLUDEPATH += "C:\DevelLibs\WpdPack\Include" INCLUDEPATH += "..\rpc" diff --git a/server/myservice.cpp b/server/myservice.cpp index de438e1..79db27b 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,7 +1,311 @@ #include "myservice.h" #include "qdebug.h" -#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} +#include + +#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} +#define MB (1024*1024) + +int StreamInfo::makePacket(uchar *buf, int bufMaxSize) +{ + int pktLen, len = 0; + uchar scratch[8]; + + // TODO(HI): use FrameLengthMode - don't assume fixed + pktLen = d.core().frame_len(); + if (bufMaxSize < pktLen) + return 0; + + // We always have a Mac Header! + // TODO(HI): use MacMode - don't assume fixed + qToBigEndian((quint64) d.mac().dst_mac(), scratch); + memcpy((buf + len), scratch + 2, 6); + len += 6; + qToBigEndian((quint64) d.mac().src_mac(), scratch); + memcpy((buf + len), &scratch + 2, 6); + len += 6; + + switch(d.core().ft()) + { + case OstProto::StreamCore::e_ft_none: + break; + case OstProto::StreamCore::e_ft_eth_2: + qToBigEndian((quint16) d.eth2().type(), buf+len); + len += 2; + break; + case OstProto::StreamCore::e_ft_802_3_raw: + qToBigEndian((quint16) pktLen, buf+len); + len += 2; + break; + case OstProto::StreamCore::e_ft_802_3_llc: + buf[len+0] = (quint8) d.llc().dsap(); + buf[len+1] = (quint8) d.llc().ssap(); + buf[len+2] = (quint8) d.llc().ctl(); + len +=3; + break; + case OstProto::StreamCore::e_ft_snap: + buf[len+0] = (quint8) d.llc().dsap(); + buf[len+1] = (quint8) d.llc().ssap(); + buf[len+2] = (quint8) d.llc().ctl(); + len +=3; + qToBigEndian((quint32) d.snap().oui(), scratch); + memcpy((buf + len), scratch + 2, 3); + len += 3; + qToBigEndian((quint16) d.eth2().type(), buf+len); + len += 2; + break; + default: + qWarning("Unhandled frame type %d\n", d.core().ft()); + } + + switch (d.core().l3_proto()) + { + case OstProto::StreamCore::e_l3_none: + break; + case OstProto::StreamCore::e_l3_ip: + buf[len+0] = (quint8) (d.ip().ver_hdrlen()); + buf[len+1] = (quint8) (d.ip().tos()); + len += 2; + qToBigEndian((quint16) d.ip().tot_len(), buf+len); + len += 2; + qToBigEndian((quint16) d.ip().id(), buf+len); + len += 2; + qToBigEndian((quint16) (( (d.ip().flags() & 0x3) << 13) | + (d.ip().frag_ofs() & 0x1FFF)), buf+len); + len += 2; + buf[len+0] = (quint8) (d.ip().ttl()); + buf[len+1] = (quint8) (d.ip().proto()); + len += 2; + qToBigEndian((quint16) d.ip().cksum(), buf+len); + len += 2; + // TODO(HI): Use IpMode - don't assume fixed + qToBigEndian((quint32) d.ip().src_ip(), buf+len); + len +=4; + qToBigEndian((quint32) d.ip().dst_ip(), buf+len); + len +=4; + break; + case OstProto::StreamCore::e_l3_arp: + // TODO(LOW) + break; + default: + qWarning("Unhandled l3 proto %d\n", d.core().l3_proto()); + } + + switch (d.core().l4_proto()) + { + case OstProto::StreamCore::e_l4_none: + break; + case OstProto::StreamCore::e_l4_tcp: + qToBigEndian((quint16) d.tcp().src_port(), buf+len); + len += 2; + qToBigEndian((quint16) d.tcp().dst_port(), buf+len); + len += 2; + qToBigEndian((quint16) d.tcp().seq_num(), buf+len); + len += 2; + qToBigEndian((quint16) d.tcp().ack_num(), buf+len); + len += 2; + buf[len+0] = (quint8) d.tcp().hdrlen_rsvd(); + buf[len+1] = (quint8) d.tcp().flags(); + len += 2; + qToBigEndian((quint16) d.tcp().window(), buf+len); + len +=2; + qToBigEndian((quint16) d.tcp().cksum(), buf+len); + len +=2; + qToBigEndian((quint16) d.tcp().urg_ptr(), buf+len); + len +=2; + break; + case OstProto::StreamCore::e_l4_udp: + qToBigEndian((quint16) d.udp().src_port(), buf+len); + len += 2; + qToBigEndian((quint16) d.udp().dst_port(), buf+len); + len += 2; + qToBigEndian((quint16) d.udp().totlen(), buf+len); + len +=2; + qToBigEndian((quint16) d.udp().cksum(), buf+len); + len +=2; + break; + case OstProto::StreamCore::e_l4_icmp: + // TODO(LOW) + break; + case OstProto::StreamCore::e_l4_igmp: + // TODO(LOW) + break; + default: + qWarning("Unhandled l4 proto %d\n", d.core().l4_proto()); + } + + // Fill-in the data pattern + { + int dataLen; + + dataLen = pktLen - len; + for (int i = 0; i < (dataLen/4)+1; i++) + { + // TODO(HI): Use patternMode + qToBigEndian((quint32) d.core().pattern(), buf+len+(i*4)); + } + } + + return pktLen; +} + +// +// ------------------ PortInfo -------------------- +// +PortInfo::PortInfo(uint id, pcap_if_t *dev) + : monitor(this) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + this->dev = dev; + devHandle = pcap_open(dev->name, 65536, PCAP_OPENFLAG_PROMISCUOUS , 1000, + NULL, errbuf); + if (devHandle == NULL) + { + qDebug("Error opening port %s: %s\n", + dev->name, pcap_geterr(devHandle)); + } + + /* By default, put the interface in statistics mode */ + if (pcap_setmode(devHandle, MODE_STAT)<0) + { + qDebug("Error setting statistics mode.\n"); + } + + d.mutable_port_id()->set_id(id); + d.set_name("eth"); // FIXME(MED): suffix portid + d.set_description(dev->description); + d.set_is_enabled(true); // FIXME(MED):check + d.set_is_oper_up(true); // FIXME(MED):check + d.set_is_exclusive_control(false); // FIXME(MED): check + + resetStats(); + + // We'll create sendqueue later when required + sendQueue = NULL; + isSendQueueDirty=true; + + // Start the monitor thread + monitor.start(); +} + +void PortInfo::update() +{ + uchar pktBuf[2000]; + pcap_pkthdr pktHdr; + + qDebug("In %s", __FUNCTION__); + + if (sendQueue) + pcap_sendqueue_destroy(sendQueue); + + // TODO(LOW): calculate sendqueue size + sendQueue = pcap_sendqueue_alloc(1*MB); + + for (int i = 0; i < streamList.size(); i++) + { + // FIXME(HI): Testing only + //if (streamList[i].d.core().is_enabled()) + { + pktHdr.len = streamList[i].makePacket(pktBuf, sizeof(pktBuf)); + pktHdr.caplen = pktHdr.len; + pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + + if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, + (u_char*) pktBuf)) + { + qDebug("[port %d] sendqueue_queue() failed for streamidx %d\n", + id(), i); + } + } + } + + isSendQueueDirty = false; +} + +void PortInfo::startTransmit() +{ + int n; + + // TODO(HI): Stream Mode - one pass/continuous + n = pcap_sendqueue_transmit(devHandle, sendQueue, false); + if (n == 0) + qDebug("port %d: send error %s\n", id(), pcap_geterr(devHandle)); + else + qDebug("port %d: sent %d bytes\n", id(), n); +} + +void PortInfo::stopTransmit() +{ +} + +void PortInfo::resetStats() +{ + memset((void*) &stats, 0, sizeof(stats)); +} + +// +// ------------------ PortMonitor ------------------- +// + +PortInfo::PortMonitor::PortMonitor(PortInfo *port) +{ + this->port = port; +} + +void PortInfo::PortMonitor::callback(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + PortInfo *port = (PortInfo*) state; + quint64 pkts; + quint64 bytes; + + pkts = *((quint64*)(pkt_data + 0)); + bytes = *((quint64*)(pkt_data + 8)); + + port->stats.rxPkts += pkts; + port->stats.rxBytes += bytes; + +#if 0 + for (int i=0; i < 16; i++) + { + qDebug("%02x ", pkt_data[i]); + } + qDebug("{%d: %llu, %llu}\n", port->id(), + pkts, bytes); + qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts); + qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes); +#endif +} + +void PortInfo::PortMonitor::run() +{ + int ret; + + qDebug("before pcap_loop\n"); + + /* Start the main loop */ + ret = pcap_loop(port->devHandle, -1, &PortInfo::PortMonitor::callback, + (PUCHAR) port); + //ret = pcap_loop(fp, -1, &updateStats, (PUCHAR)&st_ts); + + switch(ret) + { + case 0: + qDebug("Unexpected return from pcap_loop()\n"); + break; + case -1: + qDebug("Unsolicited (error) return from pcap_loop()\n"); + break; + case -2: + qDebug("Solicited return from pcap_loop()\n"); + break; + default: + qDebug("Unknown return value from pcap_loop()\n"); + } +} + +/*--------------- MyService ---------------*/ int MyService::getStreamIndex(unsigned int portIdx, unsigned int streamId) @@ -12,9 +316,9 @@ int MyService::getStreamIndex(unsigned int portIdx, Q_ASSERT(portIdx < numPorts); - for (i = 0; i < portInfo[portIdx].streamList.size(); i++) + for (i = 0; i < portInfo[portIdx]->streamList.size(); i++) { - if (streamId == portInfo[portIdx].streamList.at(i).d.stream_id().id()) + if (streamId == portInfo[portIdx]->streamList.at(i).d.stream_id().id()) goto _found; } @@ -43,34 +347,19 @@ MyService::MyService(AbstractHost *host) goto _fail; } - /* Count number of local ports */ - for(dev = alldevs; dev != NULL; dev = dev->next) + portInfo.clear(); + /* Count, Populate and Print the list */ + for(i=0, dev=alldevs; dev!=NULL; i++, dev=dev->next) + { + portInfo.append(new PortInfo(i, dev)); numPorts++; - portInfo = new PortInfo[numPorts]; - - /* Populate and Print the list */ - for(i=0, dev=alldevs; (i < numPorts) && (dev!=NULL); i++, dev=dev->next) - { - portInfo[i].setId(i); - portInfo[i].setPcapDev(dev); #if 1 LOG("%d. %s", i, dev->name); if (dev->description) { LOG(" (%s)\n", dev->description); } -#endif -#if 0 - // FIXME(HI): Testing only!!!! - { - StreamInfo s; - - s.d.mutable_stream_id()->set_id(0); - portInfo[i].streamList.append(s); - s.d.mutable_stream_id()->set_id(1); - portInfo[i].streamList.append(s); - } #endif } @@ -86,7 +375,6 @@ _fail: MyService::~MyService() { - delete portInfo; pcap_freealldevs(alldevs); } @@ -103,7 +391,7 @@ void MyService::getPortIdList( ::OstProto::PortId *p; p = response->add_port_id(); - p->set_id(portInfo[i].d.port_id().id()); + p->set_id(portInfo[i]->d.port_id().id()); } done->Run(); @@ -126,7 +414,7 @@ const ::OstProto::PortIdList* request, OstProto::Port *p; p = response->add_port(); - p->CopyFrom(portInfo[idx].d); + p->CopyFrom(portInfo[idx]->d); } } @@ -151,11 +439,11 @@ const ::OstProto::PortId* request, } response->mutable_port_id()->set_id(portIdx); - for (int j = 0; j < portInfo[portIdx].streamList.size(); j++) + for (int j = 0; j < portInfo[portIdx]->streamList.size(); j++) { OstProto::StreamId *s, *q; - q = portInfo[portIdx].streamList[j].d.mutable_stream_id(); + q = portInfo[portIdx]->streamList[j].d.mutable_stream_id(); s = response->add_stream_id(); s->CopyFrom(*q); @@ -192,7 +480,7 @@ const ::OstProto::StreamIdList* request, continue; // TODO(LOW): Partial status of RPC s = response->add_stream(); - s->CopyFrom(portInfo[portIdx].streamList[streamIndex].d); + s->CopyFrom(portInfo[portIdx]->streamList[streamIndex].d); } _exit: @@ -229,10 +517,11 @@ const ::OstProto::StreamIdList* request, // expected in a subsequent "modifyStream" request - set the stream id // now itself however!!! s.d.mutable_stream_id()->CopyFrom(request->stream_id(i)); - portInfo[portIdx].streamList.append(s); + portInfo[portIdx]->streamList.append(s); // TODO(LOW): fill-in response "Ack"???? } + portInfo[portIdx]->setDirty(true); _exit: done->Run(); } @@ -262,10 +551,11 @@ const ::OstProto::StreamIdList* request, if (streamIndex < 0) continue; // TODO(LOW): Partial status of RPC - portInfo[portIdx].streamList.removeAt(streamIndex); + portInfo[portIdx]->streamList.removeAt(streamIndex); // TODO(LOW): fill-in response "Ack"???? } + portInfo[portIdx]->setDirty(true); _exit: done->Run(); } @@ -295,11 +585,12 @@ const ::OstProto::StreamConfigList* request, if (streamIndex < 0) continue; // TODO(LOW): Partial status of RPC - portInfo[portIdx].streamList[streamIndex].d.MergeFrom( + portInfo[portIdx]->streamList[streamIndex].d.MergeFrom( request->stream(i)); // TODO(LOW): fill-in response "Ack"???? } + portInfo[portIdx]->setDirty(true); _exit: done->Run(); } @@ -310,7 +601,33 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + + // If any of the ports in the request are dirty, first update them + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // FIXME(MED): partial RPC? + + if (portInfo[portIdx]->isDirty()) + portInfo[portIdx]->update(); + } + + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // FIXME(MED): partial RPC? + + portInfo[portIdx]->startTransmit(); + } + + // TODO(LOW): fill-in response "Ack"???? + done->Run(); } @@ -320,7 +637,18 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // FIXME(MED): partial RPC? + + portInfo[portIdx]->stopTransmit(); + } + // TODO(LOW): fill-in response "Ack"???? done->Run(); } @@ -360,7 +688,30 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + + for (int i=0; i < request->port_id_size(); i++) + { + uint portidx; + ::OstProto::PortStats *s; + + portidx = request->port_id(i).id(); + if (portidx >= numPorts) + continue; // FIXME(med): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + s->set_rx_pkts(portInfo[portidx]->stats.rxPkts); + s->set_rx_bytes(portInfo[portidx]->stats.rxBytes); + s->set_rx_pps(portInfo[portidx]->stats.rxPps); + s->set_rx_bps(portInfo[portidx]->stats.rxBps); + + s->set_tx_pkts(portInfo[portidx]->stats.txPkts); + s->set_tx_bytes(portInfo[portidx]->stats.txBytes); + s->set_tx_pps(portInfo[portidx]->stats.txPps); + s->set_tx_bps(portInfo[portidx]->stats.txBps); + } + done->Run(); } @@ -370,7 +721,19 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // FIXME(MED): partial RPC? + + portInfo[portIdx]->resetStats(); + } + // TODO(LOW): fill-in response "Ack"???? + done->Run(); } diff --git a/server/myservice.h b/server/myservice.h index 45577be..69e2f71 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -10,7 +10,9 @@ #include "../common/protocol.pb.h" #include "abstracthost.h" #include +#include #include +#include #include "../rpc/pbhelper.h" @@ -22,10 +24,12 @@ class MyService; class StreamInfo { friend class MyService; + friend class PortInfo; OstProto::Stream d; StreamInfo() { PbHelper pbh; pbh.ForceSetSingularDefault(&d); } + int StreamInfo::makePacket(uchar *buf, int bufMaxSize); }; @@ -33,34 +37,65 @@ class PortInfo { friend class MyService; + class PortMonitor: public QThread + { + friend class PortInfo; + + PortInfo *port; + public: + PortMonitor(PortInfo *port); + static void callback(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data); + void run(); + }; + OstProto::Port d; + pcap_if_t *dev; + pcap_t *devHandle; + pcap_send_queue *sendQueue; + bool isSendQueueDirty; + PortMonitor monitor; + + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + struct PortStats stats; /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ QList streamList; public: - // TODO(LOW): Both setId and setPcapDev() should together form the ctor - void setId(unsigned int id) { d.mutable_port_id()->set_id(id); } - void setPcapDev(pcap_if_t *dev) - { - this->dev = dev; - d.set_name("eth"); // FIXME(MED): suffix portid - d.set_description(dev->description); - d.set_is_enabled(true); // FIXME(MED):check - d.set_is_oper_up(true); // FIXME(MED):check - d.set_is_exclusive_control(false); // FIXME(MED): check - } + PortInfo::PortInfo(uint id, pcap_if_t *dev); + uint id() { return d.port_id().id(); } + bool isDirty() { return isSendQueueDirty; } + void setDirty(bool dirty) { isSendQueueDirty = dirty; } + void update(); + void startTransmit(); + void stopTransmit(); + void resetStats(); }; + class MyService: public OstProto::OstService { AbstractHost *host; char logStr[1024]; uint numPorts; + /*! PortInfo::d::port_id and index into portInfo[] are same! */ - PortInfo *portInfo; + QList portInfo; pcap_if_t *alldevs; int getStreamIndex(unsigned int portIdx,unsigned int streamId); From 62a82dfb805dd6ba31b118a3e9d1fbec25dd220b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 28 Sep 2008 18:01:52 +0000 Subject: [PATCH 010/294] Stream creation (various modes etc.) done except for Rate Control. PortStats done - need to find solution for txRates --- client/mainwindow.cpp | 4 +- client/mainwindow.ui | 318 ++++++------- client/port.cpp | 4 + client/port.h | 1 + client/portgroup.cpp | 103 ++++- client/portgroup.h | 6 +- client/portstatsfilter.ui | 51 +- client/portstatsmodel.cpp | 51 +- client/portstatsmodel.h | 9 + client/portstatswindow.cpp | 47 +- client/portstatswindow.h | 3 +- client/portswindow.cpp | 10 + client/stream.cpp | 27 ++ client/stream.h | 82 +++- client/streamconfigdialog.cpp | 222 ++++++++- client/streamconfigdialog.h | 4 + client/streamconfigdialog.ui | 848 ++++------------------------------ client/streammodel.cpp | 12 +- common/protocol.proto | 48 +- rpc/pbrpcchannel.cpp | 31 +- rpc/rpcserver.cpp | 26 +- server/drone.pro | 2 +- server/myservice.cpp | 464 ++++++++++++++++--- server/myservice.h | 53 ++- 24 files changed, 1355 insertions(+), 1071 deletions(-) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 7eef2f3..d86b01f 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -18,10 +18,10 @@ MainWindow::MainWindow(QWidget *parent) setupUi(this); - dock->setWidget(portsWindow); - addDockWidget(Qt::TopDockWidgetArea, dock); dock2->setWidget(statsWindow); addDockWidget(Qt::BottomDockWidgetArea, dock2); + dock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, dock); } void MainWindow::on_actionPreferences_triggered() diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 5d43edb..98ab4c2 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -1,159 +1,159 @@ - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - MainWindow - - - - - - 0 - 0 - 800 - 21 - - - - - File - - - - - - View - - - - - - Window - - - - - - - - - - - - Help - - - - - - - - - - - - Open Config - - - - - Save Config - - - - - Save Config As ... - - - - - Open Capture - - - - - Save Capture - - - - - Save Capture As ... - - - - - E&xit - - - - - Copy Port Config - - - - - Paste Port Config - - - - - Preferences - - - - - Minimize - - - - - Maximize/Restore - - - - - Minimize All - - - - - Maximize/Restoe All - - - - - Arrange All - Cascade - - - - - Arrange All - Tile - - - - - Dock - - - - - Help - - - - - About - - - - - - + + MainWindow + + + + 0 + 0 + 800 + 650 + + + + MainWindow + + + + + + 0 + 0 + 800 + 21 + + + + + File + + + + + + View + + + + + + Window + + + + + + + + + + + + Help + + + + + + + + + + + + Open Config + + + + + Save Config + + + + + Save Config As ... + + + + + Open Capture + + + + + Save Capture + + + + + Save Capture As ... + + + + + E&xit + + + + + Copy Port Config + + + + + Paste Port Config + + + + + Preferences + + + + + Minimize + + + + + Maximize/Restore + + + + + Minimize All + + + + + Maximize/Restoe All + + + + + Arrange All - Cascade + + + + + Arrange All - Tile + + + + + Dock + + + + + Help + + + + + About + + + + + + diff --git a/client/port.cpp b/client/port.cpp index 06f40ae..3ee571e 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -20,6 +20,10 @@ Port::Port(quint32 id, quint32 portGroupId) mPortGroupId = portGroupId; } +Port::~Port() +{ +} + void Port::updatePortConfig(OstProto::Port *port) { d.MergeFrom(*port); diff --git a/client/port.h b/client/port.h index b16d680..c10eeb3 100644 --- a/client/port.h +++ b/client/port.h @@ -35,6 +35,7 @@ public: // FIXME(HIGH): default args is a hack for QList operations on Port Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); quint32 portGroupId() const { return mPortGroupId; } const QString& userAlias() const { return mUserAlias; } diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 70cb66e..686b436 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -82,6 +82,13 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) Q_ASSERT(portIndex < mPorts.size()); + if (state() != QAbstractSocket::ConnectedState) + { + if (cookie != NULL) + delete cookie; + return; + } + if (cookie == NULL) { // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] @@ -414,22 +421,62 @@ void PortGroup::processModifyStreamAck(OstProto::Ack *ack) // TODO(HI): Apply Button should now be disabled???!!!!??? } -void PortGroup::startTx(QList portList) +void PortGroup::startTx(QList *portList) { OstProto::PortIdList portIdList; - OstProto::Ack *ack = new OstProto::Ack; + OstProto::Ack *ack; qDebug("In %s", __FUNCTION__); - for (int i = 0; i < portList.size(); i++) + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList.at(i)); + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } } serviceStub->startTx(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processStartTxAck, ack)); +_exit: + return; +} + +void PortGroup::stopTx(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->stopTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, ack)); +_exit: + return; } void PortGroup::processStartTxAck(OstProto::Ack *ack) @@ -439,19 +486,30 @@ void PortGroup::processStartTxAck(OstProto::Ack *ack) delete ack; } -void PortGroup::getPortStats() +void PortGroup::processStopTxAck(OstProto::Ack *ack) { - OstProto::PortStatsList *portStatsList = new OstProto::PortStatsList; - qDebug("In %s", __FUNCTION__); + delete ack; +} + +void PortGroup::getPortStats() +{ + OstProto::PortStatsList *portStatsList; + + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + portStatsList = new OstProto::PortStatsList; serviceStub->getStats(rpcController, &portIdList, portStatsList, NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); } void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) { - qDebug("In %s", __FUNCTION__); + //qDebug("In %s", __FUNCTION__); if (rpcController->Failed()) { @@ -474,12 +532,30 @@ _error_exit: delete portStatsList; } -void PortGroup::clearPortStats() +void PortGroup::clearPortStats(QList *portList) { - OstProto::Ack *ack = new OstProto::Ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; qDebug("In %s", __FUNCTION__); + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + portIdList.CopyFrom(this->portIdList); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + serviceStub->clearStats(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processClearStatsAck, ack)); } @@ -488,6 +564,9 @@ void PortGroup::processClearStatsAck(OstProto::Ack *ack) { qDebug("In %s", __FUNCTION__); + // Refresh stats immediately after a stats clear/reset + getPortStats(); + delete ack; } diff --git a/client/portgroup.h b/client/portgroup.h index e2b5317..2ffcbaf 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -70,12 +70,14 @@ public: void processModifyStreamAck(OstProto::Ack *ack); - void startTx(QList portList); + void startTx(QList *portList = NULL); void processStartTxAck(OstProto::Ack *ack); + void stopTx(QList *portList = NULL); + void processStopTxAck(OstProto::Ack *ack); void getPortStats(); void processPortStatsList(OstProto::PortStatsList *portStatsList); - void clearPortStats(); + void clearPortStats(QList *portList = NULL); void processClearStatsAck(OstProto::Ack *ack); signals: diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index 8220436..e34fde2 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -5,8 +5,8 @@ 0 0 - 402 - 275 + 588 + 298 @@ -71,8 +71,55 @@ QAbstractItemView::ExtendedSelection + + QListView::Free + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + U + + + + + + + D + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index d8b40e6..b3fae30 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -105,6 +105,12 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const case e_STAT_FRAME_RECV_RATE: return stats.rx_pps(); + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + case e_STAT_BYTES_RCVD: return stats.rx_bytes(); @@ -117,7 +123,15 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const case e_STAT_BYTE_RECV_RATE: return stats.rx_bps(); + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); return 0; } } @@ -140,15 +154,38 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in void PortStatsModel::portListFromIndex(QModelIndexList indices, QList &portList) { + int i, j; + QModelIndexList selectedCols(indices); + portList.clear(); - for (int i = 0; i < indices.size(); i++) + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) { - //getDomainIndexes(indices.at(i), portGroupIdx, portIdx); + uint portGroupIdx, portIdx; - for (int j = 0; j < portList.size(); j++) + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) { - // FIXME(HI): Incomplete!!!! + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); } } } @@ -193,5 +230,9 @@ void PortStatsModel::updateStats() void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) { // FIXME(MED): update only the changed ports, not all - reset(); + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); } diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 6dc4693..1825524 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -9,10 +9,14 @@ typedef enum { e_STAT_FRAMES_SENT, e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_RECV_RATE, + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, e_STAT_BYTES_RCVD, e_STAT_BYTES_SENT, e_STAT_BYTE_SEND_RATE, e_STAT_BYTE_RECV_RATE, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, e_STAT_MAX } PortStat; @@ -21,10 +25,14 @@ static QStringList PortStatName = (QStringList() << "Frames Sent" << "Frame Send Rate (fps)" << "Frame Receive Rate (fps)" + << "Frames Received (NIC)" + << "Frames Sent (NIC)" << "Bytes Received" << "Bytes Sent" << "Byte Send Rate (Bps)" << "Byte Receive Rate (Bps)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" ); class PortGroupList; @@ -44,6 +52,7 @@ class PortStatsModel : public QAbstractTableModel int role = Qt::DisplayRole) const; class PortGroupAndPortList { + public: uint portGroupId; QList portList; }; diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 28b8f88..5f4ff6b 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -14,7 +14,10 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) model = pgl->getPortStatsModel(); tvPortStats->setModel(model); tvPortStats->horizontalHeader()->setMovable(true); - tvPortStats->verticalHeader()->resizeSections(QHeaderView::ResizeToContents); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); } PortStatsWindow::~PortStatsWindow() @@ -25,21 +28,34 @@ PortStatsWindow::~PortStatsWindow() void PortStatsWindow::on_tbStartTransmit_clicked() { - // TODO(MED): get selected ports + QList pgpl; - if (pgl->numPortGroups()) + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) { - QList portIdList; - - // FIXME(HI): Testing only!!! - portIdList.append(1); // MS Loopback adapter - pgl->portGroupByIndex(0).startTx(portIdList); + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); } } void PortStatsWindow::on_tbStopTransmit_clicked() { - // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } } void PortStatsWindow::on_tbStartCapture_clicked() @@ -59,7 +75,18 @@ void PortStatsWindow::on_tbViewCapture_clicked() void PortStatsWindow::on_tbClear_clicked() { - // TODO(MED) + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } } void PortStatsWindow::on_tbClearAll_clicked() diff --git a/client/portstatswindow.h b/client/portstatswindow.h index 98642c1..c1d581f 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -5,6 +5,7 @@ #include #include "ui_portstatswindow.h" #include "portgrouplist.h" +#include "portstatsmodel.h" class PortStatsWindow : public QWidget, public Ui::PortStatsWindow { @@ -16,7 +17,7 @@ public: private: PortGroupList *pgl; - QAbstractItemModel *model; + PortStatsModel *model; private slots: void on_tbStartTransmit_clicked(); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index f711f55..645bd1b 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -11,6 +11,12 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) setupUi(this); + tvStreamList->horizontalHeader()->resizeSections( + QHeaderView::ResizeToContents); + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate Context Menu Actions tvPortList->addAction(actionNew_Port_Group); tvPortList->addAction(actionDelete_Port_Group); tvPortList->addAction(actionConnect_Port_Group); @@ -40,6 +46,10 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); + + // Initially we don't have any ports - so trigger selection of + // portgroup detail page + when_portView_currentChanged(QModelIndex(), QModelIndex()); } PortsWindow::~PortsWindow() diff --git a/client/stream.cpp b/client/stream.cpp index 653859c..2d82040 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -951,6 +951,7 @@ Stream::Stream() mId = 0xFFFFFFFF; mCore = new OstProto::StreamCore; + mControl = new OstProto::StreamControl; // mCore->set_port_id(0xFFFFFFFF); // mCore->set_stream_id(mId); @@ -973,6 +974,8 @@ Stream::Stream() mUdp = new UdpProtocol; mIcmp = new IcmpProtocol; mIgmp = new IgmpProtocol; + + mCore->set_is_enabled(true); } void Stream::getConfig(uint portId, OstProto::Stream *s) @@ -980,6 +983,7 @@ void Stream::getConfig(uint portId, OstProto::Stream *s) s->mutable_stream_id()->set_id(mId); s->mutable_core()->CopyFrom(*mCore); + s->mutable_control()->CopyFrom(*mControl); mMac->getConfig(s->mutable_mac()); mMac->getConfig(s->mutable_mac()); @@ -997,6 +1001,29 @@ void Stream::getConfig(uint portId, OstProto::Stream *s) mIgmp->getConfig(s->mutable_igmp()); } +bool Stream::update(OstProto::Stream *stream) + { + mCore->MergeFrom(stream->core()); + mControl->MergeFrom(stream->control()); + mMac->update(stream->mac()); + + mLlc->update(stream->llc()); + mSnap->update(stream->snap()); + mEth2->update(stream->eth2()); + mVlan->update(stream->vlan()); + + mIp->update(stream->ip()); + mArp->update(stream->arp()); + + mTcp->update(stream->tcp()); + mUdp->update(stream->udp()); + mIcmp->update(stream->icmp()); + mIgmp->update(stream->igmp()); + + // FIXME(MED): Re-eval why not store complete OstProto::Stream + // instead of components + return true; + } // FIXME(HIGH): Replace this by some Protocol Registration mechanism at Init #define PTYP_L2_NONE 1 #define PTYP_L2_ETH_2 2 diff --git a/client/stream.h b/client/stream.h index 9500064..42f74ff 100644 --- a/client/stream.h +++ b/client/stream.h @@ -831,6 +831,7 @@ class Stream { quint32 mId; OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; UnknownProtocol *mUnknown; PayloadProtocol *mPayload; @@ -909,6 +910,22 @@ public: e_l4_igmp, }; + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + // ------------------------------------------------------- // Methods // ------------------------------------------------------- @@ -917,30 +934,9 @@ public: bool operator < (const Stream &s) const { return(mCore->ordinal() < s.mCore->ordinal()); } - bool update(OstProto::Stream *stream) - { - mCore->MergeFrom(stream->core()); - mMac->update(stream->mac()); - - mLlc->update(stream->llc()); - mSnap->update(stream->snap()); - mEth2->update(stream->eth2()); - mVlan->update(stream->vlan()); - - mIp->update(stream->ip()); - mArp->update(stream->arp()); - - mTcp->update(stream->tcp()); - mUdp->update(stream->udp()); - mIcmp->update(stream->icmp()); - mIgmp->update(stream->igmp()); - - // FIXME(MED): Re-eval why not store complete OstProto::Stream - // instead of components - return true; - } void getConfig(uint portId, OstProto::Stream *s); + bool update(OstProto::Stream *stream); quint32 id() { return mId;} @@ -1025,6 +1021,48 @@ public: { mCore->set_l4_proto((OstProto::StreamCore::L4Proto) l4Proto); return true; } + SendUnit sendUnit() + { return (SendUnit) mControl->unit(); } + bool setSendUnit(SendUnit sendUnit) + { mControl->set_unit( + (OstProto::StreamControl::SendUnit) sendUnit); return true; } + + SendMode sendMode() + { return (SendMode) mControl->mode(); } + bool setSendMode(SendMode sendMode) + { mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); return true; } + + NextWhat nextWhat() + { return (NextWhat) mControl->next(); } + bool setNextWhat(NextWhat nextWhat) + { mControl->set_next( + (OstProto::StreamControl::NextWhat) nextWhat); return true; } + + quint32 numPackets() + { return (quint32) mControl->num_packets(); } + bool setNumPackets(quint32 numPackets) + { mControl->set_num_packets(numPackets); return true; } + + quint32 numBursts() + { return (quint32) mControl->num_bursts(); } + bool setNumBursts(quint32 numBursts) + { mControl->set_num_bursts(numBursts); return true; } + + quint32 burstSize() + { return (quint32) mControl->packets_per_burst(); } + bool setBurstSize(quint32 packetsPerBurst) + { mControl->set_packets_per_burst(packetsPerBurst); return true; } + + quint32 packetRate() + { return (quint32) mControl->packets_per_sec(); } + bool setPacketRate(quint32 packetsPerSec) + { mControl->set_packets_per_sec(packetsPerSec); return true; } + + quint32 burstRate() + { return (quint32) mControl->bursts_per_sec(); } + bool setBurstRate(quint32 burstsPerSec) + { mControl->set_bursts_per_sec(burstsPerSec); return true; } //--------------------------------------------------------------- // Methods for use by Packet Model diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 0202825..cef9771 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -13,6 +13,112 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, setupUi(this); setupUiExtra(); + // Time to play match the signals and slots! + connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + + // Show/Hide FrameType related inputs for FT None + connect(rbFtNone, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + + // Show/Hide FrameType related inputs for FT Ethernet2 + connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); + + // Show/Hide FrameType related inputs for FT 802.3 Raw + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + + // Show/Hide FrameType related inputs for FT 802.3 LLC + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblSsap, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leSsap, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblControl, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leControl, SLOT(setVisible(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + + // Show/Hide FrameType related inputs for FT 802.3 LLC SNAP + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblSsap, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leSsap, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblControl, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leControl, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblOui, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leOui, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); + + // Enable/Disable FrameType related inputs for FT 802.3 LLC SNAP + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setDisabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setDisabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblSsap, SLOT(setDisabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leSsap, SLOT(setDisabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblControl, SLOT(setDisabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leControl, SLOT(setDisabled(bool))); + + // Enable/Disable L4 Protocol Choices for L3 Protocol None + connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), rbL4Other, SLOT(setDisabled(bool))); + + // Force L4 Protocol = None if L3 Protocol is set to None + connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); + + // Enable/Disable L4 Protocol Choices for L3 Protocol IPv4 + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setEnabled(bool))); + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setEnabled(bool))); + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setEnabled(bool))); + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setEnabled(bool))); + connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Other, SLOT(setEnabled(bool))); + + // Enable/Disable L4 Protocol Choices for L3 Protocol ARP + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Other, SLOT(setDisabled(bool))); + + // Force L4 Protocol = None if L3 Protocol is set to ARP + connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); + + // Init with FT=Eth2 to trigger signals; actual value will be + // initialized by LoadCurrentStream() + rbFtEthernet2->click(); + rbFtNone->click(); + //mpStreamList = streamList; mCurrentStreamIndex = streamIndex; LoadCurrentStream(); @@ -24,9 +130,16 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, vwPacketDump->setModel(mpPacketModel); vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); - // FIXME(MED): Enable this navigation + + + // TODO(MED): + //! \todo Enable navigation of streams pbPrev->setDisabled(true); pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + rbActionGotoStream->setDisabled(true); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); } void StreamConfigDialog::setupUiExtra() @@ -128,6 +241,22 @@ void StreamConfigDialog::on_pbNext_clicked() #endif } +void StreamConfigDialog::on_rbFtNone_toggled(bool checked) +{ +} + +void StreamConfigDialog::on_rbFtEthernet2_toggled(bool checked) +{ +} + +void StreamConfigDialog::on_rbFt802Dot3Raw_toggled(bool checked) +{ +} + +void StreamConfigDialog::on_rbFt802Dot3Llc_toggled(bool checked) +{ +} + void StreamConfigDialog::on_rbFtLlcSnap_toggled(bool checked) { if (checked) @@ -551,6 +680,55 @@ void StreamConfigDialog::LoadCurrentStream() // TODO(LOW) } } + + // Stream Control + { + switch (pStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", pStream->sendUnit()); + } + + switch (pStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", pStream->sendMode()); + } + + switch(pStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", pStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(pStream->numPackets())); + leNumBursts->setText(QString().setNum(pStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum( + pStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(pStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(pStream->burstRate())); + } } void StreamConfigDialog::StoreCurrentStream() @@ -623,11 +801,14 @@ void StreamConfigDialog::StoreCurrentStream() { // L2 | Ethernet { + qDebug("%s: LL dstMac = %llx", __FUNCTION__, + leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); pStream->mac()->setDstMac( leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); #if 1 - qDebug("%s: dstMac = %llx [%s] %d", __FUNCTION__, - pStream->mac()->dstMac(), + qDebug("%s: dstMac = %llx", __FUNCTION__, + pStream->mac()->dstMac()); + qDebug("%s: dstMac = [%s] %d", __FUNCTION__, leDstMac->text().toAscii().constData(), isOk); #endif pStream->mac()->setDstMacMode( @@ -639,8 +820,9 @@ void StreamConfigDialog::StoreCurrentStream() pStream->mac()->setSrcMac( leSrcMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); - qDebug("%s: srcMac = %llx [%s] %d", __FUNCTION__, - pStream->mac()->srcMac(), + qDebug("%s: srcMac = %llx", __FUNCTION__, + pStream->mac()->srcMac()); + qDebug("%s: srcMac = [%s] %d", __FUNCTION__, leSrcMac->text().toAscii().constData(), isOk); pStream->mac()->setSrcMacMode( (MacProtocol::MacAddrMode) cmbSrcMacMode->currentIndex()); @@ -657,7 +839,7 @@ void StreamConfigDialog::StoreCurrentStream() vlan->setCvlanPrio(cmbCvlanPrio->currentIndex()); vlan->setCvlanCfi(cmbCvlanCfi->currentIndex()); vlan->setCvlanId(leCvlanId->text().toULong(&isOk)); - vlan->setCtpid(leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk)); + vlan->setCtpid(leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk, 16)); if (cbCvlanTpidOverride->isChecked()) f |= VlanProtocol::VlanCtpidOverride; if (gbCvlan->isChecked()) @@ -666,7 +848,7 @@ void StreamConfigDialog::StoreCurrentStream() vlan->setSvlanPrio(cmbSvlanPrio->currentIndex()); vlan->setSvlanCfi(cmbSvlanCfi->currentIndex()); vlan->setSvlanId(leSvlanId->text().toULong(&isOk)); - vlan->setStpid(leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk)); + vlan->setStpid(leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk, 16)); if (cbSvlanTpidOverride->isChecked()) f |= VlanProtocol::VlanStpidOverride; if (gbSvlan->isChecked()) @@ -798,6 +980,32 @@ void StreamConfigDialog::StoreCurrentStream() // TODO(LOW) } } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } } void StreamConfigDialog::on_pbOk_clicked() diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 2485011..f95205d 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -44,6 +44,10 @@ private slots: void on_pbPrev_clicked(); void on_pbNext_clicked(); + void on_rbFtNone_toggled(bool checked); + void on_rbFtEthernet2_toggled(bool checked); + void on_rbFt802Dot3Raw_toggled(bool checked); + void on_rbFt802Dot3Llc_toggled(bool checked); void on_rbFtLlcSnap_toggled(bool checked); void on_rbL3Ipv4_toggled(bool checked); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 503eb27..f3ed791 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -33,7 +33,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 2 + 0 @@ -214,7 +214,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff None - false + true @@ -241,7 +241,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 802.3 LLC - true + false @@ -332,7 +332,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
- + Type @@ -420,7 +420,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - false + true ICMP @@ -430,7 +430,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - false + true IGMP @@ -440,7 +440,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - false + true TCP @@ -450,7 +450,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - false + true UDP @@ -2151,12 +2151,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 252 - 288 + 112 + 267 - 252 - 317 + 112 + 267 @@ -2167,12 +2167,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 252 - 388 + 112 + 267 - 252 - 417 + 112 + 267 @@ -2183,12 +2183,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 259 + 92 + 267 - 41 - 317 + 92 + 267 @@ -2199,12 +2199,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 259 + 92 + 267 - 81 - 317 + 92 + 267 @@ -2215,12 +2215,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 259 + 92 + 267 - 120 - 317 + 92 + 267 @@ -2231,12 +2231,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 259 + 92 + 267 - 252 - 288 + 112 + 267 @@ -2247,12 +2247,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 359 + 92 + 267 - 41 - 417 + 92 + 267 @@ -2263,12 +2263,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 359 + 92 + 267 - 120 - 417 + 92 + 267 @@ -2279,12 +2279,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 359 + 92 + 267 - 252 - 388 + 112 + 267 @@ -2295,12 +2295,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 31 - 359 + 92 + 267 - 81 - 417 + 92 + 267 @@ -2311,12 +2311,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 123 - 305 + 145 + 334 - 123 - 305 + 145 + 334 @@ -2327,12 +2327,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 123 - 305 + 145 + 334 - 123 - 305 + 145 + 334 @@ -2343,12 +2343,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 42 - 171 + 123 + 305 - 232 - 170 + 123 + 305 @@ -2359,12 +2359,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 42 - 197 + 123 + 305 - 232 - 196 + 123 + 305 @@ -2375,12 +2375,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 42 - 252 + 123 + 305 - 232 - 251 + 123 + 305 @@ -2391,12 +2391,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 383 - 274 + 192 + 305 - 506 - 274 + 192 + 305 @@ -2407,12 +2407,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 123 - 305 + 145 + 334 - 123 - 305 + 145 + 334 @@ -2423,492 +2423,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 123 - 305 + 145 + 334 - 123 - 305 - - - - - rbFtNone - toggled(bool) - lblDsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtNone - toggled(bool) - leDsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtNone - toggled(bool) - lblSsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtNone - toggled(bool) - leSsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtNone - toggled(bool) - lblControl - setHidden(bool) - - - 57 - 218 - - - 137 - 237 - - - - - rbFtNone - toggled(bool) - leControl - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtNone - toggled(bool) - lblOui - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtNone - toggled(bool) - leOui - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtNone - toggled(bool) - lblTpe - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtNone - toggled(bool) - leType - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtEthernet2 - toggled(bool) - lblDsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtEthernet2 - toggled(bool) - leDsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtEthernet2 - toggled(bool) - lblSsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtEthernet2 - toggled(bool) - leSsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblControl - setHidden(bool) - - - 57 - 218 - - - 137 - 237 - - - - - rbFt802Dot3Raw - toggled(bool) - leControl - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtEthernet2 - toggled(bool) - lblControl - setHidden(bool) - - - 57 - 218 - - - 137 - 237 - - - - - rbFtEthernet2 - toggled(bool) - leControl - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtEthernet2 - toggled(bool) - lblOui - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFtEthernet2 - toggled(bool) - leOui - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblDsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - leDsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblSsap - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - leSsap - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblControl - setHidden(bool) - - - 57 - 218 - - - 137 - 237 - - - - - rbFt802Dot3Raw - toggled(bool) - leControl - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblOui - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - leOui - setHidden(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - lblTpe - setHidden(bool) - - - 57 - 218 - - - 58 - 218 - - - - - rbFt802Dot3Raw - toggled(bool) - leType - setHidden(bool) - - - 57 - 218 - - - 99 - 218 + 145 + 334 @@ -2944,182 +2464,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - rbFtLlcSnap - toggled(bool) - leDsap - setDisabled(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtLlcSnap - toggled(bool) - leSsap - setDisabled(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbFtLlcSnap - toggled(bool) - leControl - setDisabled(bool) - - - 57 - 218 - - - 99 - 218 - - - - - rbL3Ipv4 - toggled(bool) - rbL4Tcp - setEnabled(bool) - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Ipv4 - toggled(bool) - rbL4Udp - setEnabled(bool) - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Ipv4 - toggled(bool) - rbL4Other - setEnabled(bool) - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Ipv4 - toggled(bool) - rbL4Icmp - setEnabled(bool) - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Ipv4 - toggled(bool) - rbL4Igmp - setEnabled(bool) - - - 570 - 218 - - - 570 - 302 - - - - - rbL3None - clicked() - rbL4None - click() - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Arp - clicked() - rbL4None - click() - - - 570 - 218 - - - 570 - 302 - - - - - rbL3Other - clicked() - rbL4None - click() - - - 570 - 218 - - - 570 - 302 - - - rbActionGotoStream toggled(bool) @@ -3127,12 +2471,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 381 - 140 + 136 + 120 - 381 - 174 + 136 + 120 @@ -3143,12 +2487,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 69 + 41 + 101 - 85 - 285 + 96 + 120 @@ -3159,12 +2503,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 94 + 41 + 120 - 67 - 331 + 78 + 120 @@ -3175,12 +2519,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 94 + 41 + 120 - 222 - 189 + 136 + 120 diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 3ee9136..32dce31 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -38,7 +38,10 @@ Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const flags |= Qt::ItemIsEditable; break; case StreamStatus: +#if 0 flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable; +#endif + flags |= Qt::ItemIsEditable; break; default: break; @@ -87,11 +90,11 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } case StreamStatus: { - #if 0 if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return streamList[index.row()].isEnabled; - if ((role == Qt::DisplayRole) || (role == Qt::CheckStateRole) || - #endif + return mCurrentPort->streamByIndex(index.row()).isEnabled(); + else + return QVariant(); + #if 0 if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) { if (mCurrentPort->streamByIndex(index.row()).isEnabled()) @@ -101,6 +104,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } else return QVariant(); + #endif break; } default: diff --git a/common/protocol.proto b/common/protocol.proto index 7ffd54f..fee57b9 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -109,7 +109,7 @@ message Tcp { optional uint32 dst_port = 4 [default = 80]; optional uint32 seq_num = 5 [default = 129018]; - optional uint32 ack_num = 6 [default = 98223]; + optional uint32 ack_num = 6; optional uint32 hdrlen_rsvd = 7 [default = 0x50]; optional uint32 flags = 8; @@ -138,6 +138,9 @@ message Icmp { message Igmp { } +message StreamId { + required uint32 id = 1; +} message StreamCore { @@ -201,14 +204,41 @@ message StreamCore { optional L4Proto l4_proto = 23; } -message StreamId { - required uint32 id = 1; +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7; + optional uint32 bursts_per_sec = 8; + + // TODO: Gaps? + } message Stream { required StreamId stream_id = 1; optional StreamCore core = 2; + optional StreamControl control = 3; // Protocol data - L2 optional Mac mac = 51; @@ -283,13 +313,17 @@ message PortStats { optional uint64 rx_pkts = 11; optional uint64 rx_bytes = 12; - optional uint64 rx_pps = 13; - optional uint64 rx_bps = 14; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; optional uint64 tx_pkts = 21; optional uint64 tx_bytes = 22; - optional uint64 tx_pps = 23; - optional uint64 tx_bps = 24; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; } message PortStatsList { diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 366ca6f..ed60d2f 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -69,7 +69,7 @@ void PbRpcChannel::CallMethod( char *p = (char *)&msg; int len; - qDebug("In %s", __FUNCTION__); + //qDebug("In %s", __FUNCTION__); if (!req->IsInitialized()) { @@ -88,8 +88,8 @@ void PbRpcChannel::CallMethod( isPending = true; *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_REQUEST); // type - qDebug("CLi:GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), - PB_MSG_TYPE_REQUEST); + //qDebug("CLi:GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), + //PB_MSG_TYPE_REQUEST); *((quint16*)(p+2)) = HTONS(method->index()); // method id // (p+4) len later after serialization *((quint16*)(p+6)) = HTONS(0); // rsvd @@ -100,9 +100,13 @@ void PbRpcChannel::CallMethod( len = req->ByteSize(); *((quint16*)(p+4)) = HTONS(len); // len - qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, len+8, - req->DebugString().c_str()); - BUFDUMP(msg, len+8); + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 12) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, len+8, + req->DebugString().c_str()); + BUFDUMP(msg, len+8); + } mpSocket->write(msg, len + 8); } @@ -115,12 +119,12 @@ void PbRpcChannel::on_mpSocket_readyRead() quint16 type, method, len, rsvd; PbRpcController *controller; - qDebug("In %s", __FUNCTION__); + //qDebug("In %s", __FUNCTION__); msgLen = mpSocket->read(msg, sizeof(msg)); - qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); - BUFDUMP(msg, msgLen); + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); type = NTOHS(GET16(p+0)); method = NTOHS(GET16(p+2)); @@ -153,8 +157,13 @@ void PbRpcChannel::on_mpSocket_readyRead() // Serialized data starts from offset 8 response->ParseFromArray((void*) &msg[8], len); - qDebug("client(%s): Parsed as %s", __FUNCTION__, - response->DebugString().c_str()); + + // Avoid printing stats + if (method != 12) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } if (!response->IsInitialized()) { diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 7b6335b..a3978cb 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -41,7 +41,7 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo char *p = (char *)&msg; int len; - qDebug("In RpcServer::done"); + //qDebug("In RpcServer::done"); // TODO: check PbRpcController to see if method failed if (PbRpcController->Failed()) @@ -67,9 +67,13 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo len = resp->ByteSize(); (*(quint16*)(p+4)) = HTONS(len); // len - qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + 8, resp->DebugString().c_str()); - //BUFDUMP(msg, len + 8); + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 12) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + 8, resp->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } clientSock->write(msg, len + 8); @@ -132,17 +136,17 @@ void RpcServer::when_dataAvail() PbRpcController *controller; msgLen = clientSock->read(msg, sizeof(msg)); - LogInt(QString(QByteArray(msg, msgLen).toHex())); + //LogInt(QString(QByteArray(msg, msgLen).toHex())); - qDebug("Server %s: rcvd %d bytes", __FUNCTION__, msgLen); + //qDebug("Server %s: rcvd %d bytes", __FUNCTION__, msgLen); //BUFDUMP(msg, msgLen); type = NTOHS(GET16(p+0)); method = NTOHS(GET16(p+2)); len = NTOHS(GET16(p+4)); rsvd = NTOHS(GET16(p+6)); - qDebug("type = %d, method = %d, len = %d, rsvd = %d", - type, method, len, rsvd); + //qDebug("type = %d, method = %d, len = %d, rsvd = %d", + //type, method, len, rsvd); if (type != PB_MSG_TYPE_REQUEST) { @@ -182,12 +186,12 @@ void RpcServer::when_dataAvail() goto _error_exit; } - qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, - resp->DebugString().c_str()); + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); controller = new PbRpcController; - qDebug("before service->callmethod()"); + //qDebug("before service->callmethod()"); service->CallMethod(methodDesc, controller, req, resp, NewCallback(this, &RpcServer::done, resp, controller)); diff --git a/server/drone.pro b/server/drone.pro index 79cbd1a..3642823 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -5,7 +5,7 @@ DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "c:\msys\1.0\local\include" INCLUDEPATH += "C:\DevelLibs\WpdPack\Include" INCLUDEPATH += "..\rpc" -LIBS += -L"C:\DevelLibs\WpdPack\Lib" -lwpcap +LIBS += -L"C:\DevelLibs\WpdPack\Lib" -lwpcap -lpacket LIBS += -L"..\rpc\debug" -lpbrpc HEADERS += drone.h FORMS += drone.ui diff --git a/server/myservice.cpp b/server/myservice.cpp index 79db27b..b21310d 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,30 +1,177 @@ #include "myservice.h" #include "qdebug.h" +#include #include +#ifdef Q_OS_WIN32 +#include +#include +#endif + #define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} #define MB (1024*1024) -int StreamInfo::makePacket(uchar *buf, int bufMaxSize) +#if 0 +quint16 StreamInfo::ipv4Cksum(quint16 ipHdrLen, quint16 buff[]) { - int pktLen, len = 0; + quint16 word16; + quint32 sum=0; + quint16 i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet + // and add them up + for (i = 0; i < ipHdrLen ;i += 2) + { + word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF); + sum = sum + (quint32) word16; + } + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum>>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return (quint16) sum; +} +#endif + +quint16 StreamInfo::ipv4Cksum(uchar *buf, int len) +{ + quint32 sum = 0; /* assume 32 bit long, 16 bit short */ + quint16 *ip = (quint16*) buf; + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) /* if high order bit set, fold */ + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if(len) /* take care of left over byte */ + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + qDebug("cksum = %x", ~sum); + return (quint16) ~sum; +} + +int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) +{ + int u, pktLen, len = 0; uchar scratch[8]; // TODO(HI): use FrameLengthMode - don't assume fixed - pktLen = d.core().frame_len(); + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen = d.core().frame_len() - 4; if (bufMaxSize < pktLen) return 0; // We always have a Mac Header! - // TODO(HI): use MacMode - don't assume fixed - qToBigEndian((quint64) d.mac().dst_mac(), scratch); + switch (d.mac().dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + qToBigEndian((quint64) d.mac().dst_mac(), scratch); + break; + case OstProto::Mac::e_mm_inc: + u = (n % d.mac().dst_mac_count()) * d.mac().dst_mac_step(); + qToBigEndian((quint64) d.mac().dst_mac() + u, scratch); + break; + case OstProto::Mac::e_mm_dec: + u = (n % d.mac().dst_mac_count()) * d.mac().dst_mac_step(); + qToBigEndian((quint64) d.mac().dst_mac() - u, scratch); + break; + default: + qWarning("Unhandled dstMac_mode %d", d.mac().dst_mac_mode()); + } memcpy((buf + len), scratch + 2, 6); len += 6; - qToBigEndian((quint64) d.mac().src_mac(), scratch); - memcpy((buf + len), &scratch + 2, 6); + + switch (d.mac().src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + qToBigEndian((quint64) d.mac().src_mac(), scratch); + break; + case OstProto::Mac::e_mm_inc: + u = (n % d.mac().src_mac_count()) * d.mac().src_mac_step(); + qToBigEndian((quint64) d.mac().src_mac() + u, scratch); + break; + case OstProto::Mac::e_mm_dec: + u = (n % d.mac().src_mac_count()) * d.mac().src_mac_step(); + qToBigEndian((quint64) d.mac().src_mac() - u, scratch); + break; + default: + qWarning("Unhandled srcMac_mode %d", d.mac().src_mac_mode()); + } + memcpy((buf + len), scratch + 2, 6); len += 6; + + // Frame Type - Part 1 (pre VLAN info) + switch(d.core().ft()) + { + case OstProto::StreamCore::e_ft_none: + case OstProto::StreamCore::e_ft_eth_2: + break; + case OstProto::StreamCore::e_ft_802_3_raw: + qToBigEndian((quint16) pktLen, buf+len); + len += 2; + break; + case OstProto::StreamCore::e_ft_802_3_llc: + qToBigEndian((quint16) pktLen, buf+len); + len += 2; + buf[len+0] = (quint8) d.llc().dsap(); + buf[len+1] = (quint8) d.llc().ssap(); + buf[len+2] = (quint8) d.llc().ctl(); + len +=3; + break; + case OstProto::StreamCore::e_ft_snap: + qToBigEndian((quint16) pktLen, buf+len); + len += 2; + buf[len+0] = (quint8) d.llc().dsap(); + buf[len+1] = (quint8) d.llc().ssap(); + buf[len+2] = (quint8) d.llc().ctl(); + len +=3; + qToBigEndian((quint32) d.snap().oui(), scratch); + memcpy((buf + len), scratch + 2, 3); + len += 3; + break; + default: + qWarning("Unhandled frame type %d\n", d.core().ft()); + } + + // VLAN + if (d.vlan().is_svlan_tagged()) + { + if (d.vlan().is_stpid_override()) + qToBigEndian((quint16) d.vlan().stpid(), buf+len); + else + qToBigEndian((quint16) 0x88a8, buf+len); + len += 2 ; + + qToBigEndian((quint16) d.vlan().svlan_tag(), buf+len); + len += 2 ; + } + + if (d.vlan().is_cvlan_tagged()) + { + if (d.vlan().is_ctpid_override()) + qToBigEndian((quint16) d.vlan().ctpid(), buf+len); + else + qToBigEndian((quint16) 0x8100, buf+len); + len += 2 ; + + qToBigEndian((quint16) d.vlan().cvlan_tag(), buf+len); + len += 2 ; + } + + // Frame Type - Part 2 (post VLAN info) switch(d.core().ft()) { case OstProto::StreamCore::e_ft_none: @@ -34,23 +181,9 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize) len += 2; break; case OstProto::StreamCore::e_ft_802_3_raw: - qToBigEndian((quint16) pktLen, buf+len); - len += 2; - break; case OstProto::StreamCore::e_ft_802_3_llc: - buf[len+0] = (quint8) d.llc().dsap(); - buf[len+1] = (quint8) d.llc().ssap(); - buf[len+2] = (quint8) d.llc().ctl(); - len +=3; break; case OstProto::StreamCore::e_ft_snap: - buf[len+0] = (quint8) d.llc().dsap(); - buf[len+1] = (quint8) d.llc().ssap(); - buf[len+2] = (quint8) d.llc().ctl(); - len +=3; - qToBigEndian((quint32) d.snap().oui(), scratch); - memcpy((buf + len), scratch + 2, 3); - len += 3; qToBigEndian((quint16) d.eth2().type(), buf+len); len += 2; break; @@ -58,32 +191,107 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize) qWarning("Unhandled frame type %d\n", d.core().ft()); } + // L3 switch (d.core().l3_proto()) { case OstProto::StreamCore::e_l3_none: break; case OstProto::StreamCore::e_l3_ip: + { + quint32 subnet, host; + int ipOfs = len; + buf[len+0] = (quint8) (d.ip().ver_hdrlen()); buf[len+1] = (quint8) (d.ip().tos()); len += 2; - qToBigEndian((quint16) d.ip().tot_len(), buf+len); + + if (d.ip().is_override_totlen()) + qToBigEndian((quint16) d.ip().tot_len(), buf+len); + else + qToBigEndian((quint16) (pktLen - ipOfs), buf+len); len += 2; + qToBigEndian((quint16) d.ip().id(), buf+len); len += 2; + qToBigEndian((quint16) (( (d.ip().flags() & 0x3) << 13) | (d.ip().frag_ofs() & 0x1FFF)), buf+len); len += 2; + buf[len+0] = (quint8) (d.ip().ttl()); buf[len+1] = (quint8) (d.ip().proto()); len += 2; - qToBigEndian((quint16) d.ip().cksum(), buf+len); + + // cksum calculated after filling in the rest + qToBigEndian((quint16) 0, buf+len); len += 2; + // TODO(HI): Use IpMode - don't assume fixed - qToBigEndian((quint32) d.ip().src_ip(), buf+len); + switch(d.ip().src_ip_mode()) + { + case OstProto::Ip::e_im_fixed: + qToBigEndian((quint32) d.ip().src_ip(), buf+len); + break; + case OstProto::Ip::e_im_inc_host: + u = n % d.ip().src_ip_count(); + subnet = d.ip().src_ip() & d.ip().src_ip_mask(); + host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) + u) & + ~d.ip().src_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + case OstProto::Ip::e_im_dec_host: + u = n % d.ip().src_ip_count(); + subnet = d.ip().src_ip() & d.ip().src_ip_mask(); + host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) - u) & + ~d.ip().src_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + case OstProto::Ip::e_im_random_host: + subnet = d.ip().src_ip() & d.ip().src_ip_mask(); + host = (qrand() & ~d.ip().src_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + default: + qWarning("Unhandled src_ip_mode = %d", d.ip().src_ip_mode()); + } len +=4; - qToBigEndian((quint32) d.ip().dst_ip(), buf+len); + + switch(d.ip().dst_ip_mode()) + { + case OstProto::Ip::e_im_fixed: + qToBigEndian((quint32) d.ip().dst_ip(), buf+len); + break; + case OstProto::Ip::e_im_inc_host: + u = n % d.ip().dst_ip_count(); + subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); + host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) + u) & + ~d.ip().dst_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + case OstProto::Ip::e_im_dec_host: + u = n % d.ip().dst_ip_count(); + subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); + host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) - u) & + ~d.ip().dst_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + case OstProto::Ip::e_im_random_host: + subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); + host = (qrand() & ~d.ip().dst_ip_mask()); + qToBigEndian((quint32) (subnet | host), buf+len); + break; + default: + qWarning("Unhandled dst_ip_mode = %d", d.ip().dst_ip_mode()); + } len +=4; + + // Calculate and fill in cksum (unless overridden) + if (d.ip().is_override_cksum()) + qToBigEndian((quint16) d.ip().cksum(), buf+ipOfs+10); + else + *((quint16*)(buf + ipOfs + 10)) = ipv4Cksum(buf + ipOfs, len-ipOfs); break; + } case OstProto::StreamCore::e_l3_arp: // TODO(LOW) break; @@ -96,34 +304,59 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize) case OstProto::StreamCore::e_l4_none: break; case OstProto::StreamCore::e_l4_tcp: + { qToBigEndian((quint16) d.tcp().src_port(), buf+len); len += 2; qToBigEndian((quint16) d.tcp().dst_port(), buf+len); len += 2; - qToBigEndian((quint16) d.tcp().seq_num(), buf+len); - len += 2; - qToBigEndian((quint16) d.tcp().ack_num(), buf+len); - len += 2; - buf[len+0] = (quint8) d.tcp().hdrlen_rsvd(); + + qToBigEndian((quint32) d.tcp().seq_num(), buf+len); + len += 4; + qToBigEndian((quint32) d.tcp().ack_num(), buf+len); + len += 4; + + if (d.tcp().is_override_hdrlen()) + buf[len+0] = (quint8) d.tcp().hdrlen_rsvd(); + else + buf[len+0] = (quint8) 0x50; // FIXME(LOW) buf[len+1] = (quint8) d.tcp().flags(); len += 2; + qToBigEndian((quint16) d.tcp().window(), buf+len); len +=2; - qToBigEndian((quint16) d.tcp().cksum(), buf+len); + + if (d.tcp().is_override_cksum()) + qToBigEndian((quint16) d.tcp().cksum(), buf+len); + else + qToBigEndian((quint16) 0, buf+len); // FIXME(HI) len +=2; + qToBigEndian((quint16) d.tcp().urg_ptr(), buf+len); len +=2; break; + } case OstProto::StreamCore::e_l4_udp: + { + int udpLen = pktLen - len; + qToBigEndian((quint16) d.udp().src_port(), buf+len); len += 2; qToBigEndian((quint16) d.udp().dst_port(), buf+len); len += 2; - qToBigEndian((quint16) d.udp().totlen(), buf+len); + + if (d.udp().is_override_totlen()) + qToBigEndian((quint16) d.udp().totlen(), buf+len); + else + qToBigEndian((quint16) udpLen, buf+len); len +=2; - qToBigEndian((quint16) d.udp().cksum(), buf+len); + + if (d.udp().is_override_cksum()) + qToBigEndian((quint16) d.udp().cksum(), buf+len); + else + qToBigEndian((quint16) 0, buf+len); // FIXME(HI) len +=2; break; + } case OstProto::StreamCore::e_l4_icmp: // TODO(LOW) break; @@ -149,6 +382,7 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize) return pktLen; } + // // ------------------ PortInfo -------------------- // @@ -166,6 +400,13 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) dev->name, pcap_geterr(devHandle)); } +#if 0 + if (pcap_setdirection(devHandle, PCAP_D_IN)<0) + { + qDebug("[%s] Error setting direction inbound only\n", dev->name); + } +#endif + /* By default, put the interface in statistics mode */ if (pcap_setmode(devHandle, MODE_STAT)<0) { @@ -179,10 +420,14 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) d.set_is_oper_up(true); // FIXME(MED):check d.set_is_exclusive_control(false); // FIXME(MED): check + memset((void*) &stats, 0, sizeof(stats)); resetStats(); // We'll create sendqueue later when required sendQueue = NULL; + pcapExtra.sendQueueNumPkts = 0; + pcapExtra.txPkts = 0; + pcapExtra.txBytes = 0; isSendQueueDirty=true; // Start the monitor thread @@ -201,21 +446,47 @@ void PortInfo::update() // TODO(LOW): calculate sendqueue size sendQueue = pcap_sendqueue_alloc(1*MB); + pcapExtra.sendQueueNumPkts = 0; + + // First sort the streams by ordinalValue + qSort(streamList); for (int i = 0; i < streamList.size(); i++) { - // FIXME(HI): Testing only - //if (streamList[i].d.core().is_enabled()) + if (streamList[i].d.core().is_enabled()) { - pktHdr.len = streamList[i].makePacket(pktBuf, sizeof(pktBuf)); - pktHdr.caplen = pktHdr.len; - pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + int numPackets, numBursts; - if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, - (u_char*) pktBuf)) + if (streamList[i].d.control().unit() == + OstProto::StreamControl::e_su_bursts) { - qDebug("[port %d] sendqueue_queue() failed for streamidx %d\n", - id(), i); + numBursts = streamList[i].d.control().num_bursts(); + numPackets = streamList[i].d.control().packets_per_burst(); + } + else + { + numBursts = 1; + numPackets = streamList[i].d.control().num_packets(); + } + + for (int j = 0; j < numBursts; j++) + { + for (int k = 0; k < numPackets; k++) + { + pktHdr.len = streamList[i].makePacket( + pktBuf, sizeof(pktBuf), j * numPackets + k); + pktHdr.caplen = pktHdr.len; + pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + + if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, + (u_char*) pktBuf)) + { + qDebug("[port %d] sendqueue_queue() failed for " + "streamidx %d\n", id(), i); + } + else + pcapExtra.sendQueueNumPkts++; + } } } } @@ -225,14 +496,29 @@ void PortInfo::update() void PortInfo::startTransmit() { - int n; + uint bytes; // TODO(HI): Stream Mode - one pass/continuous - n = pcap_sendqueue_transmit(devHandle, sendQueue, false); - if (n == 0) - qDebug("port %d: send error %s\n", id(), pcap_geterr(devHandle)); + bytes = pcap_sendqueue_transmit(devHandle, sendQueue, false); + if (bytes < sendQueue->len) + { + qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", + id(), bytes, sendQueue->len, pcap_geterr(devHandle)); + //! \TODO parse sendqueue using 'bytes' to get actual pkts sent + pcapExtra.txPkts += pcapExtra.sendQueueNumPkts; + pcapExtra.txBytes += bytes; + } else - qDebug("port %d: sent %d bytes\n", id(), n); + { + qDebug("port %d: sent (%d/%d) bytes\n", id(), bytes, sendQueue->len); + pcapExtra.txPkts += pcapExtra.sendQueueNumPkts; + pcapExtra.txBytes += bytes; + } + + // pcap_sendqueue_transmit() returned 'bytes' includes size of pcap_pkthdr + // - adjust for it + pcapExtra.txBytes -= pcapExtra.sendQueueNumPkts * sizeof(pcap_pkthdr); + } void PortInfo::stopTransmit() @@ -241,7 +527,7 @@ void PortInfo::stopTransmit() void PortInfo::resetStats() { - memset((void*) &stats, 0, sizeof(stats)); + memcpy((void*) &epochStats, (void*) &stats, sizeof(stats)); } // @@ -251,21 +537,57 @@ void PortInfo::resetStats() PortInfo::PortMonitor::PortMonitor(PortInfo *port) { this->port = port; +#ifdef Q_OS_WIN32 + { + int sz = sizeof(PACKET_OID_DATA) + sizeof(quint64) + 4; + //oidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, + //sizeof(PACKET_OID_DATA) + sizeof(quint64) - 1); + oidData = (PPACKET_OID_DATA) malloc(sz); + if (oidData) + { + memset(oidData, 0, sz); + oidData->Length=sizeof(quint64); + } + else + qFatal("failed to alloc oidData"); + } +#endif } void PortInfo::PortMonitor::callback(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data) { + uint usec; PortInfo *port = (PortInfo*) state; + quint64 pkts; quint64 bytes; - pkts = *((quint64*)(pkt_data + 0)); - bytes = *((quint64*)(pkt_data + 8)); + // Update RxStats and RxRates using PCAP data + pkts = *((quint64*)(pkt_data + 0)); + bytes = *((quint64*)(pkt_data + 8)); + + // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it + bytes -= pkts * ETH_FRAME_HDR_SIZE; + + usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTs.tv_usec); + port->stats.rxPps = (pkts * 1000000) / usec; + port->stats.rxBps = (bytes * 1000000) / usec; port->stats.rxPkts += pkts; port->stats.rxBytes += bytes; + // Update TxStats from PcapExtra + port->stats.txPkts = port->pcapExtra.txPkts; + port->stats.txBytes = port->pcapExtra.txBytes; + + //! \TODO TxRates + + // Store curr timestamp as last timestamp + port->lastTs.tv_sec = header->ts.tv_sec; + port->lastTs.tv_usec = header->ts.tv_usec; + #if 0 for (int i=0; i < 16; i++) { @@ -276,6 +598,26 @@ void PortInfo::PortMonitor::callback(u_char *state, qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts); qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes); #endif + + // Retreive NIC stats +#ifdef Q_OS_WIN32 + port->monitor.oidData->Oid = OID_GEN_RCV_OK; + if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) + { + if (port->monitor.oidData->Length <= sizeof(port->stats.txPkts)) + memcpy((void*)&port->stats.rxPktsNic, + (void*)port->monitor.oidData->Data, + port->monitor.oidData->Length); + } + port->monitor.oidData->Oid = OID_GEN_XMIT_OK; + if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) + { + if (port->monitor.oidData->Length <= sizeof(port->stats.txPkts)) + memcpy((void*)&port->stats.txPktsNic, + (void*)port->monitor.oidData->Data, + port->monitor.oidData->Length); + } +#endif } void PortInfo::PortMonitor::run() @@ -687,7 +1029,7 @@ const ::OstProto::PortIdList* request, ::OstProto::PortStatsList* response, ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + //qDebug("In %s", __PRETTY_FUNCTION__); for (int i=0; i < request->port_id_size(); i++) { @@ -701,13 +1043,25 @@ const ::OstProto::PortIdList* request, s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); - s->set_rx_pkts(portInfo[portidx]->stats.rxPkts); - s->set_rx_bytes(portInfo[portidx]->stats.rxBytes); + s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - + portInfo[portidx]->epochStats.rxPkts); + s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - + portInfo[portidx]->epochStats.rxBytes); + s->set_rx_pkts_nic(portInfo[portidx]->stats.rxPktsNic - + portInfo[portidx]->epochStats.rxPktsNic); + s->set_rx_bytes_nic(portInfo[portidx]->stats.rxBytesNic - + portInfo[portidx]->epochStats.rxBytesNic); s->set_rx_pps(portInfo[portidx]->stats.rxPps); s->set_rx_bps(portInfo[portidx]->stats.rxBps); - s->set_tx_pkts(portInfo[portidx]->stats.txPkts); - s->set_tx_bytes(portInfo[portidx]->stats.txBytes); + s->set_tx_pkts(portInfo[portidx]->stats.txPkts - + portInfo[portidx]->epochStats.txPkts); + s->set_tx_bytes(portInfo[portidx]->stats.txBytes - + portInfo[portidx]->epochStats.txBytes); + s->set_tx_pkts_nic(portInfo[portidx]->stats.txPktsNic - + portInfo[portidx]->epochStats.txPktsNic); + s->set_tx_bytes_nic(portInfo[portidx]->stats.txBytesNic - + portInfo[portidx]->epochStats.txBytesNic); s->set_tx_pps(portInfo[portidx]->stats.txPps); s->set_tx_bps(portInfo[portidx]->stats.txBps); } diff --git a/server/myservice.h b/server/myservice.h index 69e2f71..ecb474e 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -16,9 +16,16 @@ #include "../rpc/pbhelper.h" +#ifdef Q_OS_WIN32 +#include +#endif + #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 +//! 7 byte Preamble + 1 byte SFD + 4 byte FCS +#define ETH_FRAME_HDR_SIZE 12 + class MyService; class StreamInfo @@ -29,7 +36,13 @@ class StreamInfo OstProto::Stream d; StreamInfo() { PbHelper pbh; pbh.ForceSetSingularDefault(&d); } - int StreamInfo::makePacket(uchar *buf, int bufMaxSize); + + //quint16 ipv4Cksum(quint16 ipHdrLen, quint16 buff[]); + quint16 ipv4Cksum(uchar *buf, int len); + int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n); +public: + bool operator < (const StreamInfo &s) const + { return(d.core().ordinal() < s.d.core().ordinal()); } }; @@ -41,7 +54,10 @@ class PortInfo { friend class PortInfo; - PortInfo *port; + PortInfo *port; +#ifdef Q_OS_WIN32 + PPACKET_OID_DATA oidData; +#endif public: PortMonitor(PortInfo *port); static void callback(u_char *state, @@ -51,26 +67,47 @@ class PortInfo OstProto::Port d; - pcap_if_t *dev; - pcap_t *devHandle; - pcap_send_queue *sendQueue; - bool isSendQueueDirty; - PortMonitor monitor; - struct PortStats { quint64 rxPkts; quint64 rxBytes; + quint64 rxPktsNic; + quint64 rxBytesNic; quint64 rxPps; quint64 rxBps; quint64 txPkts; quint64 txBytes; + quint64 txPktsNic; + quint64 txBytesNic; quint64 txPps; quint64 txBps; }; + //! \todo Need lock for stats access/update + + //! Stuff we need to maintain since PCAP doesn't as of now. As and when + // PCAP supports it, we'll remove from here + struct PcapExtra + { + //! pcap_sendqueue_transmit() only returns 'bytes' sent + uint sendQueueNumPkts; + + //! PCAP doesn't do any tx stats + quint64 txPkts; + quint64 txBytes; + }; + + pcap_if_t *dev; + pcap_t *devHandle; + pcap_send_queue *sendQueue; + bool isSendQueueDirty; + PcapExtra pcapExtra; + PortMonitor monitor; + + struct PortStats epochStats; struct PortStats stats; + struct timeval lastTs; //! used for Rate Stats calculations /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ QList streamList; From bfc0e8d4c89a80317d93451bdeda1cf1e79f5279 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 5 Oct 2008 17:07:33 +0000 Subject: [PATCH 011/294] Tcp/Udp checksums done. Frame Length Modes done. Data Pattern Modes done. Some minor fixes/enhancements in streamconfigdialog. Added a "Edit Stream" action in StreamList context menu --- client/portgroup.cpp | 1 + client/portstatsmodel.cpp | 2 +- client/portswindow.cpp | 51 ++++-- client/portswindow.h | 1 + client/portswindow.ui | 8 + client/stream.h | 6 +- client/streamconfigdialog.cpp | 196 ++++++++++++++++------ client/streamconfigdialog.h | 5 +- client/streamconfigdialog.ui | 81 ++++++---- common/protocol.proto | 10 +- server/myservice.cpp | 297 ++++++++++++++++++++++++---------- server/myservice.h | 12 +- 12 files changed, 478 insertions(+), 192 deletions(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 686b436..4630c72 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -159,6 +159,7 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) case 3: qDebug("apply completed"); + mPorts[portIndex].when_syncComplete(); delete cookie; break; diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index b3fae30..d554687 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -12,7 +12,7 @@ PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); - timer->start(5000); + timer->start(2000); } int PortStatsModel::rowCount(const QModelIndex &parent) const diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 645bd1b..27dec05 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -23,6 +23,7 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvPortList->addAction(actionDisconnect_Port_Group); tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDelete_Stream); tvStreamList->setModel(plm->getStreamModel()); @@ -32,10 +33,12 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portModel_dataChanged(const QModelIndex&, const QModelIndex&))); + connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portView_currentChanged(const QModelIndex&, const QModelIndex&))); + connect( tvStreamList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_streamView_currentChanged(const QModelIndex&, @@ -43,13 +46,16 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) connect( tvStreamList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(when_streamView_selectionChanged())); + +#if 0 connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); +#endif - // Initially we don't have any ports - so trigger selection of - // portgroup detail page + // Initially we don't have any ports/streams - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); + when_streamView_currentChanged(QModelIndex(), QModelIndex()); } PortsWindow::~PortsWindow() @@ -66,11 +72,6 @@ void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) qDebug("%s: invalid index", __FUNCTION__); return; } -#if 0 // CleanedUp! - // FIXME(MED): This way of passing params must be changed - scd = new StreamConfigDialog(plm->getStreamModel()->currentPortStreamList(), - (uint) index.row(), this); -#endif scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), index.row(), this); qDebug("stream list activated\n"); @@ -81,7 +82,9 @@ void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) void PortsWindow::when_portView_currentChanged(const QModelIndex& current, const QModelIndex& previous) { + plm->getStreamModel()->setCurrentPortIndex(current); updatePortViewActions(current); + updateStreamViewActions(); if (!current.isValid()) { @@ -143,16 +146,32 @@ void PortsWindow::updateStreamViewActions(const QModelIndex& current) void PortsWindow::updateStreamViewActions() { - if (tvStreamList->selectionModel()->hasSelection()) + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) { qDebug("Has selection %d", tvStreamList->selectionModel()->selection().size()); - // If more than one non-contiguous ranges selected, disable "New" + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" if (tvStreamList->selectionModel()->selection().size() > 1) + { actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } else + { actionNew_Stream->setEnabled(true); + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + // Delete is always enabled as long as we have a selection actionDelete_Stream->setEnabled(true); } @@ -160,6 +179,7 @@ void PortsWindow::updateStreamViewActions() { qDebug("No selection"); actionNew_Stream->setEnabled(true); + actionEdit_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } } @@ -346,6 +366,19 @@ void PortsWindow::on_actionNew_Stream_triggered() plm->getStreamModel()->insertRows(row, count); } +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + void PortsWindow::on_actionDelete_Stream_triggered() { qDebug("Delete Stream Action"); diff --git a/client/portswindow.h b/client/portswindow.h index 2a26c2b..a5bc562 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -49,6 +49,7 @@ private slots: void on_actionDisconnect_Port_Group_triggered(); void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); void on_actionDelete_Stream_triggered(); }; diff --git a/client/portswindow.ui b/client/portswindow.ui index 04349d2..99a32af 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -206,6 +206,14 @@ Delete Stream + + + :/icons/stream_edit.png + + + Edit Stream + + diff --git a/client/stream.h b/client/stream.h index 42f74ff..6e41702 100644 --- a/client/stream.h +++ b/client/stream.h @@ -883,9 +883,9 @@ public: }; enum DataPatternMode { - e_dp_fixed, - e_dp_inc, - e_dp_dec, + e_dp_fixed_word, + e_dp_inc_byte, + e_dp_dec_byte, e_dp_random }; diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index cef9771..7ee59f7 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -12,6 +12,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, { setupUi(this); setupUiExtra(); + + // setupUi // Time to play match the signals and slots! connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); @@ -28,6 +30,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtNone, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); connect(rbFtNone, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + // Enable/Disable L3 Protocol Choices for FT None + connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); + // Show/Hide FrameType related inputs for FT Ethernet2 connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); @@ -40,6 +47,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); + // Enable/Disable L3 Protocol Choices for FT Ethernet2 + connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); + connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); + // Show/Hide FrameType related inputs for FT 802.3 Raw connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); @@ -52,6 +64,14 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + // Force L3 = None if FT = 802.3 Raw + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + + // Enable/Disable L3 Protocol Choices for FT 802Dot3Raw + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); + connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); + // Show/Hide FrameType related inputs for FT 802.3 LLC connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); @@ -64,6 +84,14 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); + // Force L3 = None if FT = 802.3 LLC (to ensure a valid L3 is selected) + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + + // Enable/Disable L3 Protocol Choices for FT 802Dot3Llc + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); + connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); + // Show/Hide FrameType related inputs for FT 802.3 LLC SNAP connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); @@ -76,6 +104,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); + // Enable/Disable L3 Protocol Choices for FT 802.3 LLC SNAP + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); + connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); + // Enable/Disable FrameType related inputs for FT 802.3 LLC SNAP connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setDisabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setDisabled(bool))); @@ -90,7 +123,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL3None, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); connect(rbL3None, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); connect(rbL3None, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), rbL4Other, SLOT(setDisabled(bool))); // Force L4 Protocol = None if L3 Protocol is set to None connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); @@ -101,7 +133,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setEnabled(bool))); - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Other, SLOT(setEnabled(bool))); // Enable/Disable L4 Protocol Choices for L3 Protocol ARP connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); @@ -109,15 +140,20 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Other, SLOT(setDisabled(bool))); // Force L4 Protocol = None if L3 Protocol is set to ARP connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); - // Init with FT=Eth2 to trigger signals; actual value will be - // initialized by LoadCurrentStream() + //TODO: remove if not needed +#if 0 + // This set of 'clicks' is a hack to trigger signals at dialog creation + // time so that a coherent 'set' is initialized + // Actual stream values will be initialized by LoadCurrentStream() + rbL3Ipv4->click(); + rbL3None->click(); rbFtEthernet2->click(); rbFtNone->click(); +#endif //mpStreamList = streamList; mCurrentStreamIndex = streamIndex; @@ -148,7 +184,21 @@ void StreamConfigDialog::setupUiExtra() QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); - // Setup default stuff that cannot be done in designer + // ---- Setup default stuff that cannot be done in designer ---- + + // Since the dialog defaults are FT = None, L3 = None, L4 = None; + // hide associated input fields since it can't be done in Designer + lblDsap->setHidden(true); + leDsap->setHidden(true); + lblSsap->setHidden(true); + leSsap->setHidden(true); + lblControl->setHidden(true); + leControl->setHidden(true); + lblOui->setHidden(true); + leOui->setHidden(true); + lblType->setHidden(true); + leType->setHidden(true); + twProto->setTabEnabled(2, FALSE); twProto->setTabEnabled(3, FALSE); @@ -189,17 +239,73 @@ StreamConfigDialog::~StreamConfigDialog() delete mpPacketModel; } +void StreamConfigDialog::on_cmbPatternMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed Word") + { + lePattern->setEnabled(true); + } + else if (mode == "Increment Byte") + { + lePattern->setDisabled(true); + } + else if (mode == "Decrement Byte") + { + lePattern->setDisabled(true); + } + if (mode == "Random") + { + lePattern->setDisabled(true); + } + else + { + qWarning("Unhandled/Unknown PatternMode = %s", mode.toAscii().data()); + } +} +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + + void StreamConfigDialog::on_cmbDstMacMode_currentIndexChanged(QString mode) { if (mode == "Fixed") { - leDstMacCount->setEnabled(FALSE); - leDstMacStep->setEnabled(FALSE); + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); } else { - leDstMacCount->setEnabled(TRUE); - leDstMacStep->setEnabled(TRUE); + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); } } @@ -207,13 +313,41 @@ void StreamConfigDialog::on_cmbSrcMacMode_currentIndexChanged(QString mode) { if (mode == "Fixed") { - leSrcMacCount->setEnabled(FALSE); - leSrcMacStep->setEnabled(FALSE); + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); } else { - leSrcMacCount->setEnabled(TRUE); - leSrcMacStep->setEnabled(TRUE); + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + +void StreamConfigDialog::on_cmbIpSrcAddrMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void StreamConfigDialog::on_cmbIpDstAddrMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); } } @@ -371,14 +505,6 @@ void StreamConfigDialog::on_rbL4Udp_toggled(bool checked) } } -void StreamConfigDialog::on_rbL4Other_toggled(bool checked) -{ - if (checked) - leIpProto->setEnabled(true); - else - leIpProto->setEnabled(false); -} - void StreamConfigDialog::update_NumPacketsAndNumBursts() { if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) @@ -512,32 +638,6 @@ void StreamConfigDialog::LoadCurrentStream() qDebug("%s: unknown l4 Protocol %d", __FUNCTION__, pStream->l4Proto()); } -// PB (not needed anymore?) -#if 0 - // Check for specific supported protocols first ... - if (pStream->eth2()->type() == ETH_TYP_IP) - rbL3Ipv4->setChecked(TRUE); - else if (pStream->eth2()->type() == ETH_TYP_ARP) - rbL3Arp->setChecked(TRUE); - - // ... then for None/Other - rbL3None->setChecked((pStream->proto.protoMask & PM_L3_PROTO_NONE) > 0); - rbL3Other->setChecked((pStream->proto.protoMask & PM_L3_PROTO_OTHER) > 0); - - // Check for specific supported protocols first ... - if (pStream->proto.ipProto == IP_PROTO_ICMP) - rbL4Icmp->setChecked(TRUE); - else if (pStream->proto.ipProto == IP_PROTO_IGMP) - rbL4Igmp->setChecked(TRUE); - else if (pStream->proto.ipProto == IP_PROTO_TCP) - rbL4Tcp->setChecked(TRUE); - else if (pStream->proto.ipProto == IP_PROTO_UDP) - rbL4Udp->setChecked(TRUE); - - // ... then for None/Other - rbL4None->setChecked((pStream->proto.protoMask & PM_L4_PROTO_NONE) > 0); - rbL4Other->setChecked((pStream->proto.protoMask & PM_L4_PROTO_OTHER) > 0); -#endif } // L2 diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index f95205d..a8fe510 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -39,8 +39,12 @@ private: void StoreCurrentStream(); private slots: + void on_cmbPatternMode_currentIndexChanged(QString mode); + void on_cmbPktLenMode_currentIndexChanged(QString mode); void on_cmbDstMacMode_currentIndexChanged(QString mode); void on_cmbSrcMacMode_currentIndexChanged(QString mode); + void on_cmbIpSrcAddrMode_currentIndexChanged(QString mode); + void on_cmbIpDstAddrMode_currentIndexChanged(QString mode); void on_pbPrev_clicked(); void on_pbNext_clicked(); @@ -57,7 +61,6 @@ private slots: void on_rbL4Igmp_toggled(bool checked); void on_rbL4Tcp_toggled(bool checked); void on_rbL4Udp_toggled(bool checked); - void on_rbL4Other_toggled(bool checked); void update_NumPacketsAndNumBursts(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index f3ed791..b94e107 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,7 +8,7 @@ 0 0 - 554 + 534 521 @@ -63,17 +63,17 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - Fixed + Fixed Word - Increment + Increment Byte - Decrement + Decrement Byte @@ -141,6 +141,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + 64 @@ -174,6 +177,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + 64 @@ -373,6 +379,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + IPv4 @@ -383,18 +392,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - ARP - - - - - false - Other + ARP @@ -420,7 +422,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - true + false ICMP @@ -430,7 +432,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - true + false IGMP @@ -440,7 +442,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - true + false TCP @@ -449,21 +451,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - true - - - UDP - - - - - false - Other + UDP @@ -483,6 +475,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -1054,7 +1059,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + @@ -1130,7 +1135,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + @@ -1206,6 +1211,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + @@ -1213,6 +1221,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + 255.255.255.255 @@ -1264,6 +1275,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + @@ -1271,6 +1285,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + false + 255.255.255.255 @@ -1279,7 +1296,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + @@ -1310,14 +1327,14 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Qt::Vertical - 470 + 469 16 @@ -1355,7 +1372,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 1 + 0 diff --git a/common/protocol.proto b/common/protocol.proto index fee57b9..04c7a70 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -169,9 +169,9 @@ message StreamCore { } enum DataPatternMode { - e_dp_fixed = 0; - e_dp_inc = 1; - e_dp_dec = 2; + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; e_dp_random = 3; } @@ -195,8 +195,8 @@ message StreamCore { // Frame Length (includes CRC) optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; optional uint32 frame_len = 15 [default = 64]; - optional uint32 frame_len_min = 16; - optional uint32 frame_len_max = 17; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; // Currently Selected Protocols optional FrameType ft = 21 [default = e_ft_none]; diff --git a/server/myservice.cpp b/server/myservice.cpp index b21310d..3296a86 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -12,65 +12,105 @@ #define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} #define MB (1024*1024) -#if 0 -quint16 StreamInfo::ipv4Cksum(quint16 ipHdrLen, quint16 buff[]) +quint32 StreamInfo::pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, + quint8 protocol, quint16 len) { - quint16 word16; - quint32 sum=0; - quint16 i; - - // make 16 bit words out of every two adjacent 8 bit words in the packet - // and add them up - for (i = 0; i < ipHdrLen ;i += 2) - { - word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF); - sum = sum + (quint32) word16; - } - - // take only 16 bits out of the 32 bit sum and add up the carries - while (sum>>16) - sum = (sum & 0xFFFF)+(sum >> 16); + quint32 sum; - // one's complement the result - sum = ~sum; - - return (quint16) sum; + sum = srcIp >> 16; + sum += srcIp & 0xFFFF; + sum += dstIp >> 16; + sum += dstIp & 0xFFFF; + sum += (quint16) (protocol); + sum += len; + + // Above calculation done assuming 'big endian' - so convert to host order + return qFromBigEndian(sum); } -#endif -quint16 StreamInfo::ipv4Cksum(uchar *buf, int len) +quint32 StreamInfo::ipv4CksumPartial(uchar *buf, int len) { - quint32 sum = 0; /* assume 32 bit long, 16 bit short */ + quint32 sum = 0; quint16 *ip = (quint16*) buf; - while(len > 1) + if (len & 0x0001) + { + qFatal("Cannot calculate partial checksum on non multiple of 2 length"); + return 0; + } + + while(len) { sum += *ip; - if(sum & 0x80000000) /* if high order bit set, fold */ + if(sum & 0x80000000) sum = (sum & 0xFFFF) + (sum >> 16); ip++; len -= 2; } - if(len) /* take care of left over byte */ + return sum; +} + +quint16 StreamInfo::ipv4Cksum(uchar *buf, int len, quint32 partialSum) +{ + quint32 sum = partialSum; + quint16 *ip = (quint16*) buf; + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) sum += (unsigned short) *(unsigned char *)ip; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); - qDebug("cksum = %x", ~sum); return (quint16) ~sum; } int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) { - int u, pktLen, len = 0; + int u, pktLen, dataLen, len = 0; + quint32 srcIp, dstIp; // need it later for TCP/UDP cksum calculation + quint32 cumCksum = 0; // cumulative cksum used to combine partial cksums + int tcpOfs, udpOfs; // needed to fill in cksum later uchar scratch[8]; - // TODO(HI): use FrameLengthMode - don't assume fixed + // Decide a frame length based on length mode + switch(d.core().len_mode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = d.core().frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = d.core().frame_len_min() + (n % + (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = d.core().frame_len_max() - (n % + (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + pktLen = d.core().frame_len_min() + (qrand() % + (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + d.core().len_mode()); + pktLen = 64; + break; + } + // pktLen is adjusted for CRC/FCS which will be added by the NIC - pktLen = d.core().frame_len() - 4; - if (bufMaxSize < pktLen) + pktLen -= 4; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) return 0; // We always have a Mac Header! @@ -226,30 +266,34 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) qToBigEndian((quint16) 0, buf+len); len += 2; - // TODO(HI): Use IpMode - don't assume fixed + // Get Src/Dst IP for this packet using respective IpMode switch(d.ip().src_ip_mode()) { case OstProto::Ip::e_im_fixed: - qToBigEndian((quint32) d.ip().src_ip(), buf+len); + srcIp = (quint32) d.ip().src_ip(); + qToBigEndian(srcIp, buf+len); break; case OstProto::Ip::e_im_inc_host: u = n % d.ip().src_ip_count(); subnet = d.ip().src_ip() & d.ip().src_ip_mask(); host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) + u) & ~d.ip().src_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + srcIp = (quint32) (subnet | host); + qToBigEndian(srcIp, buf+len); break; case OstProto::Ip::e_im_dec_host: u = n % d.ip().src_ip_count(); subnet = d.ip().src_ip() & d.ip().src_ip_mask(); host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) - u) & ~d.ip().src_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + srcIp = (quint32) (subnet | host); + qToBigEndian(srcIp, buf+len); break; case OstProto::Ip::e_im_random_host: subnet = d.ip().src_ip() & d.ip().src_ip_mask(); host = (qrand() & ~d.ip().src_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + srcIp = (quint32) (subnet | host); + qToBigEndian(srcIp, buf+len); break; default: qWarning("Unhandled src_ip_mode = %d", d.ip().src_ip_mode()); @@ -259,26 +303,30 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) switch(d.ip().dst_ip_mode()) { case OstProto::Ip::e_im_fixed: - qToBigEndian((quint32) d.ip().dst_ip(), buf+len); + dstIp = (quint32) d.ip().dst_ip(); + qToBigEndian(dstIp, buf+len); break; case OstProto::Ip::e_im_inc_host: u = n % d.ip().dst_ip_count(); subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) + u) & ~d.ip().dst_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + dstIp = (quint32) (subnet | host); + qToBigEndian(dstIp, buf+len); break; case OstProto::Ip::e_im_dec_host: u = n % d.ip().dst_ip_count(); subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) - u) & ~d.ip().dst_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + dstIp = (quint32) (subnet | host); + qToBigEndian(dstIp, buf+len); break; case OstProto::Ip::e_im_random_host: subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); host = (qrand() & ~d.ip().dst_ip_mask()); - qToBigEndian((quint32) (subnet | host), buf+len); + dstIp = (quint32) (subnet | host); + qToBigEndian(dstIp, buf+len); break; default: qWarning("Unhandled dst_ip_mode = %d", d.ip().dst_ip_mode()); @@ -291,6 +339,7 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) else *((quint16*)(buf + ipOfs + 10)) = ipv4Cksum(buf + ipOfs, len-ipOfs); break; + } case OstProto::StreamCore::e_l3_arp: // TODO(LOW) @@ -305,6 +354,10 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) break; case OstProto::StreamCore::e_l4_tcp: { + tcpOfs = len; + + cumCksum = pseudoHdrCksumPartial(srcIp, dstIp, 6, pktLen - len); + qToBigEndian((quint16) d.tcp().src_port(), buf+len); len += 2; qToBigEndian((quint16) d.tcp().dst_port(), buf+len); @@ -318,26 +371,30 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) if (d.tcp().is_override_hdrlen()) buf[len+0] = (quint8) d.tcp().hdrlen_rsvd(); else - buf[len+0] = (quint8) 0x50; // FIXME(LOW) + buf[len+0] = (quint8) 0x50; // FIXME(LOW): Hardcoding buf[len+1] = (quint8) d.tcp().flags(); len += 2; qToBigEndian((quint16) d.tcp().window(), buf+len); len +=2; - if (d.tcp().is_override_cksum()) - qToBigEndian((quint16) d.tcp().cksum(), buf+len); - else - qToBigEndian((quint16) 0, buf+len); // FIXME(HI) + // Fill in cksum as 0 for cksum calculation, actual cksum filled later + qToBigEndian((quint16) 0, buf+len); len +=2; qToBigEndian((quint16) d.tcp().urg_ptr(), buf+len); len +=2; + + // Accumulate cumulative cksum + cumCksum += ipv4CksumPartial(buf + tcpOfs, len - tcpOfs); + break; } case OstProto::StreamCore::e_l4_udp: { - int udpLen = pktLen - len; + udpOfs = len; + + cumCksum = pseudoHdrCksumPartial(srcIp, dstIp, 17, pktLen - len); qToBigEndian((quint16) d.udp().src_port(), buf+len); len += 2; @@ -347,14 +404,16 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) if (d.udp().is_override_totlen()) qToBigEndian((quint16) d.udp().totlen(), buf+len); else - qToBigEndian((quint16) udpLen, buf+len); + qToBigEndian((quint16) (pktLen - udpOfs), buf+len); len +=2; - if (d.udp().is_override_cksum()) - qToBigEndian((quint16) d.udp().cksum(), buf+len); - else - qToBigEndian((quint16) 0, buf+len); // FIXME(HI) + // Fill in cksum as 0 for cksum calculation, actual cksum filled later + qToBigEndian((quint16) 0, buf+len); len +=2; + + // Accumulate cumulative cksum + cumCksum += ipv4CksumPartial(buf + udpOfs, len - udpOfs); + break; } case OstProto::StreamCore::e_l4_icmp: @@ -368,15 +427,51 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) } // Fill-in the data pattern + dataLen = pktLen - len; + switch(d.core().pattern_mode()) { - int dataLen; + case OstProto::StreamCore::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) d.core().pattern(), buf+len+(i*4)); + break; + case OstProto::StreamCore::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + buf[len + i] = i % (0xFF + 1); + break; + case OstProto::StreamCore::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + buf[len + i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::StreamCore::e_dp_random: + for (int i = 0; i < dataLen; i++) + buf[len + i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", d.core().pattern_mode()); + } - dataLen = pktLen - len; - for (int i = 0; i < (dataLen/4)+1; i++) - { - // TODO(HI): Use patternMode - qToBigEndian((quint32) d.core().pattern(), buf+len+(i*4)); - } + // Calculate TCP/UDP checksum over the data pattern/payload and fill in + switch (d.core().l4_proto()) + { + case OstProto::StreamCore::e_l4_tcp: + if (d.tcp().is_override_cksum()) + qToBigEndian((quint16) d.tcp().cksum(), buf + tcpOfs + 16); + else + *((quint16*)(buf + tcpOfs + 16)) = + ipv4Cksum(buf + len, dataLen, cumCksum); + break; + case OstProto::StreamCore::e_l4_udp: + if (d.udp().is_override_cksum()) + qToBigEndian((quint16) d.udp().cksum(), buf + udpOfs + 6); + else + *((quint16*)(buf + udpOfs + 6)) = + ipv4Cksum(buf + len, dataLen, cumCksum); + break; + case OstProto::StreamCore::e_l4_none: + case OstProto::StreamCore::e_l4_icmp: + case OstProto::StreamCore::e_l4_igmp: + // No cksum processing required + break; } return pktLen; @@ -414,7 +509,7 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) } d.mutable_port_id()->set_id(id); - d.set_name("eth"); // FIXME(MED): suffix portid + d.set_name(QString("eth%1").arg(id).toAscii().constData()); d.set_description(dev->description); d.set_is_enabled(true); // FIXME(MED):check d.set_is_oper_up(true); // FIXME(MED):check @@ -425,7 +520,7 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) // We'll create sendqueue later when required sendQueue = NULL; - pcapExtra.sendQueueNumPkts = 0; + pcapExtra.sendQueueCumLen.clear(); pcapExtra.txPkts = 0; pcapExtra.txBytes = 0; isSendQueueDirty=true; @@ -446,7 +541,7 @@ void PortInfo::update() // TODO(LOW): calculate sendqueue size sendQueue = pcap_sendqueue_alloc(1*MB); - pcapExtra.sendQueueNumPkts = 0; + pcapExtra.sendQueueCumLen.clear(); // First sort the streams by ordinalValue qSort(streamList); @@ -457,35 +552,45 @@ void PortInfo::update() { int numPackets, numBursts; - if (streamList[i].d.control().unit() == - OstProto::StreamControl::e_su_bursts) + switch (streamList[i].d.control().unit()) { + case OstProto::StreamControl::e_su_bursts: numBursts = streamList[i].d.control().num_bursts(); numPackets = streamList[i].d.control().packets_per_burst(); - } - else - { + break; + case OstProto::StreamControl::e_su_packets: numBursts = 1; numPackets = streamList[i].d.control().num_packets(); + break; + default: + qWarning("Unhandled stream control unit %d", + streamList[i].d.control().unit()); + continue; } + for (int j = 0; j < numBursts; j++) { for (int k = 0; k < numPackets; k++) { - pktHdr.len = streamList[i].makePacket( - pktBuf, sizeof(pktBuf), j * numPackets + k); - pktHdr.caplen = pktHdr.len; - pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + int len; - if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, - (u_char*) pktBuf)) + len = streamList[i].makePacket(pktBuf, sizeof(pktBuf), + j * numPackets + k); + if (len > 0) { - qDebug("[port %d] sendqueue_queue() failed for " - "streamidx %d\n", id(), i); + pktHdr.caplen = pktHdr.len = len; + pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + + if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, + (u_char*) pktBuf)) + { + qDebug("[port %d] sendqueue_queue() failed for " + "streamidx %d\n", id(), i); + } + else + pcapExtra.sendQueueCumLen.append(sendQueue->len); } - else - pcapExtra.sendQueueNumPkts++; } } } @@ -496,7 +601,7 @@ void PortInfo::update() void PortInfo::startTransmit() { - uint bytes; + uint bytes, pkts; // TODO(HI): Stream Mode - one pass/continuous bytes = pcap_sendqueue_transmit(devHandle, sendQueue, false); @@ -504,21 +609,35 @@ void PortInfo::startTransmit() { qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", id(), bytes, sendQueue->len, pcap_geterr(devHandle)); - //! \TODO parse sendqueue using 'bytes' to get actual pkts sent - pcapExtra.txPkts += pcapExtra.sendQueueNumPkts; - pcapExtra.txBytes += bytes; + + // parse sendqueue using 'bytes' to get actual pkts sent +#if 0 + // FIXME(LOW): Get this working + pkts = qUpperBound(pcapExtra.sendQueueCumLen, bytes); +#else + for (int i = 0; i < pcapExtra.sendQueueCumLen.size(); i++) + { + if (pcapExtra.sendQueueCumLen.at(i) > bytes) + { + pkts = i; + break; + } + } +#endif } else { qDebug("port %d: sent (%d/%d) bytes\n", id(), bytes, sendQueue->len); - pcapExtra.txPkts += pcapExtra.sendQueueNumPkts; - pcapExtra.txBytes += bytes; + pkts = pcapExtra.sendQueueCumLen.size(); } // pcap_sendqueue_transmit() returned 'bytes' includes size of pcap_pkthdr // - adjust for it - pcapExtra.txBytes -= pcapExtra.sendQueueNumPkts * sizeof(pcap_pkthdr); + if (bytes) + bytes -= pkts * sizeof(pcap_pkthdr); + pcapExtra.txPkts += pkts; + pcapExtra.txBytes += bytes; } void PortInfo::stopTransmit() @@ -951,7 +1070,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // FIXME(MED): partial RPC? + continue; // TODO(LOW): partial RPC? if (portInfo[portIdx]->isDirty()) portInfo[portIdx]->update(); @@ -963,7 +1082,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // FIXME(MED): partial RPC? + continue; // TODO(LOW): partial RPC? portInfo[portIdx]->startTransmit(); } @@ -986,7 +1105,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // FIXME(MED): partial RPC? + continue; // TODO(LOW): partial RPC? portInfo[portIdx]->stopTransmit(); } @@ -1038,7 +1157,7 @@ const ::OstProto::PortIdList* request, portidx = request->port_id(i).id(); if (portidx >= numPorts) - continue; // FIXME(med): partial rpc? + continue; // TODO(LOW): partial rpc? s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); @@ -1082,7 +1201,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // FIXME(MED): partial RPC? + continue; // TODO(LOW): partial RPC? portInfo[portIdx]->resetStats(); } diff --git a/server/myservice.h b/server/myservice.h index ecb474e..a14524d 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -37,8 +37,10 @@ class StreamInfo StreamInfo() { PbHelper pbh; pbh.ForceSetSingularDefault(&d); } - //quint16 ipv4Cksum(quint16 ipHdrLen, quint16 buff[]); - quint16 ipv4Cksum(uchar *buf, int len); + quint32 pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, + quint8 protocol, quint16 len); + quint32 ipv4CksumPartial(uchar *buf, int len); + quint16 ipv4Cksum(uchar *buf, int len, quint32 partialSum = 0); int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n); public: bool operator < (const StreamInfo &s) const @@ -90,8 +92,10 @@ class PortInfo // PCAP supports it, we'll remove from here struct PcapExtra { - //! pcap_sendqueue_transmit() only returns 'bytes' sent - uint sendQueueNumPkts; + //! Used to track num of packets (and their sizes) in the + // send queue. Also used to find out actual num of pkts sent + // in case of partial send in pcap_sendqueue_transmit() + QList sendQueueCumLen; //! PCAP doesn't do any tx stats quint64 txPkts; From d9aa2e43a017d8c65d152dd05076df1e57aa78c9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Jan 2009 10:13:46 +0000 Subject: [PATCH 012/294] Changes for successful compilation in Linux. PCAP/Winpcap functions changed to use those which are common on both PCAP and WinPCAP. Some additional WinPCAP only functions (such as the pcap_sendqueue_xxx) which we intend to use have been added into pcapextra.c which will be used in case of Linux --- client/dumpview.h | 2 +- client/ostinato.pro | 4 +-- client/portgrouplist.h | 2 +- rpc/pbhelper.h | 2 +- server/drone.pro | 14 ++++---- server/myservice.cpp | 75 ++++++++++++++++++++++++++++++++++++------ server/myservice.h | 7 ++-- 7 files changed, 82 insertions(+), 24 deletions(-) diff --git a/client/dumpview.h b/client/dumpview.h index cdbcde7..e362103 100644 --- a/client/dumpview.h +++ b/client/dumpview.h @@ -26,7 +26,7 @@ protected slots: void paintEvent(QPaintEvent *event); private: - void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + void populateDump(QByteArray &dump, int &selOfs, int &selSize, QModelIndex parent = QModelIndex()); bool inline isPrintable(char c) {if ((c > 48) && (c < 126)) return true; else return false; } diff --git a/client/ostinato.pro b/client/ostinato.pro index 4b1cb69..7e6c89e 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,8 +1,8 @@ TEMPLATE = app CONFIG += qt debug QT += network -INCLUDEPATH += "c:\msys\1.0\local\include" "..\rpc\" -LIBS += -L"c:\msys\1.0\local\lib" -lprotobuf -L"..\rpc\debug" -lpbrpc +INCLUDEPATH += "../rpc/" +LIBS += -lprotobuf -L"../rpc/" -lpbrpc RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/client/portgrouplist.h b/client/portgrouplist.h index aadd24d..89256eb 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -26,7 +26,7 @@ class PortGroupList : public QObject { // Methods public: - PortGroupList::PortGroupList(); + PortGroupList(); PortModel* getPortModel() { return &mPortGroupListModel; } diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index 21afefa..e3d85b0 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -4,7 +4,7 @@ #include #include -#include +#include class PbHelper { diff --git a/server/drone.pro b/server/drone.pro index 3642823..315a0e2 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,15 +1,17 @@ TEMPLATE = app -CONFIG += qt +CONFIG += qt debug QT += network DEFINES += HAVE_REMOTE WPCAP -INCLUDEPATH += "c:\msys\1.0\local\include" -INCLUDEPATH += "C:\DevelLibs\WpdPack\Include" -INCLUDEPATH += "..\rpc" -LIBS += -L"C:\DevelLibs\WpdPack\Lib" -lwpcap -lpacket -LIBS += -L"..\rpc\debug" -lpbrpc +INCLUDEPATH += "../rpc" +win32:LIBS += -lwpcap -lpacket +unix:LIBS += -lpcap +win32:LIBS += -L"../rpc/debug" -lpbrpc +unix:LIBS += -L"../rpc" -lpbrpc HEADERS += drone.h FORMS += drone.ui SOURCES += drone_main.cpp drone.cpp SOURCES += myservice.cpp +unix:SOURCES += pcapextra.cpp + SOURCES += "..\common\protocol.pb.cc" diff --git a/server/myservice.cpp b/server/myservice.cpp index 3296a86..5f69adc 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -487,8 +487,8 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) char errbuf[PCAP_ERRBUF_SIZE]; this->dev = dev; - devHandle = pcap_open(dev->name, 65536, PCAP_OPENFLAG_PROMISCUOUS , 1000, - NULL, errbuf); + devHandle = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , + 1000 /*ms*/, errbuf); if (devHandle == NULL) { qDebug("Error opening port %s: %s\n", @@ -509,8 +509,16 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) } d.mutable_port_id()->set_id(id); - d.set_name(QString("eth%1").arg(id).toAscii().constData()); - d.set_description(dev->description); +#ifdef Q_OS_WIN32 + d.set_name(QString("if%1").arg(id).toAscii().constData()); +#else + if (dev->name) + d.set_name(dev->name); + else + d.set_name(QString("if%1").arg(id).toAscii().constData()); +#endif + if (dev->description) + d.set_description(dev->description); d.set_is_enabled(true); // FIXME(MED):check d.set_is_oper_up(true); // FIXME(MED):check d.set_is_exclusive_control(false); // FIXME(MED): check @@ -635,9 +643,18 @@ void PortInfo::startTransmit() // - adjust for it if (bytes) bytes -= pkts * sizeof(pcap_pkthdr); - +#ifdef Q_OS_WIN32 + // Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together pcapExtra.txPkts += pkts; pcapExtra.txBytes += bytes; +#else + // We don't have a regular stats callback function here, so update + // Port TxStats directly here + stats.txPkts += pkts; + stats.txBytes += bytes; +#endif } void PortInfo::stopTransmit() @@ -673,9 +690,12 @@ PortInfo::PortMonitor::PortMonitor(PortInfo *port) #endif } +#ifdef Q_OS_WIN32 void PortInfo::PortMonitor::callback(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data) { + // This is the WinPcap Callback - which is a 'stats mode' callback + uint usec; PortInfo *port = (PortInfo*) state; @@ -723,7 +743,7 @@ void PortInfo::PortMonitor::callback(u_char *state, port->monitor.oidData->Oid = OID_GEN_RCV_OK; if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) { - if (port->monitor.oidData->Length <= sizeof(port->stats.txPkts)) + if (port->monitor.oidData->Length <= sizeof(port->stats.rxPktsNic)) memcpy((void*)&port->stats.rxPktsNic, (void*)port->monitor.oidData->Data, port->monitor.oidData->Length); @@ -731,14 +751,50 @@ void PortInfo::PortMonitor::callback(u_char *state, port->monitor.oidData->Oid = OID_GEN_XMIT_OK; if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) { - if (port->monitor.oidData->Length <= sizeof(port->stats.txPkts)) + if (port->monitor.oidData->Length <= sizeof(port->stats.txPktsNic)) memcpy((void*)&port->stats.txPktsNic, (void*)port->monitor.oidData->Data, port->monitor.oidData->Length); } #endif } +#else +void PortInfo::PortMonitor::callback(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + // This is the LibPcap Callback - which is a 'capture mode' callback + // This callback is called once for EVERY packet + uint usec; + PortInfo *port = (PortInfo*) state; + + quint64 pkts; + quint64 bytes; + + // Update RxStats and RxRates using PCAP data + usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTs.tv_usec); + // TODO(rate) +#if 0 + port->stats.rxPps = (pkts * 1000000) / usec; + port->stats.rxBps = (bytes * 1000000) / usec; +#endif + + // Note: For a 'capture callback' PCAP reported bytes DOES NOT include + // ETH_FRAME_HDR_SIZE - so don't adjust for it + port->stats.rxPkts++; + port->stats.rxBytes += header->len; + + // NOTE: Port TxStats are updated by Port Transmit Function itself + // since this callback is called only when a packet is received + + //! \TODO TxRates + + // Store curr timestamp as last timestamp + port->lastTs.tv_sec = header->ts.tv_sec; + port->lastTs.tv_usec = header->ts.tv_usec; +} +#endif void PortInfo::PortMonitor::run() { int ret; @@ -747,8 +803,7 @@ void PortInfo::PortMonitor::run() /* Start the main loop */ ret = pcap_loop(port->devHandle, -1, &PortInfo::PortMonitor::callback, - (PUCHAR) port); - //ret = pcap_loop(fp, -1, &updateStats, (PUCHAR)&st_ts); + (u_char*) port); switch(ret) { @@ -802,7 +857,7 @@ MyService::MyService(AbstractHost *host) alldevs = NULL; LOG("Retrieving the device list from the local machine\n"); - if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) + if (pcap_findalldevs(&alldevs, errbuf) == -1) { LOG("Error in pcap_findalldevs_ex: %s\n", errbuf); goto _fail; diff --git a/server/myservice.h b/server/myservice.h index a14524d..c0679fb 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -15,7 +15,8 @@ #include #include "../rpc/pbhelper.h" - +#include "pcapextra.h" + #ifdef Q_OS_WIN32 #include #endif @@ -41,7 +42,7 @@ class StreamInfo quint8 protocol, quint16 len); quint32 ipv4CksumPartial(uchar *buf, int len); quint16 ipv4Cksum(uchar *buf, int len, quint32 partialSum = 0); - int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n); + int makePacket(uchar *buf, int bufMaxSize, int n); public: bool operator < (const StreamInfo &s) const { return(d.core().ordinal() < s.d.core().ordinal()); } @@ -117,7 +118,7 @@ class PortInfo QList streamList; public: - PortInfo::PortInfo(uint id, pcap_if_t *dev); + PortInfo(uint id, pcap_if_t *dev); uint id() { return d.port_id().id(); } bool isDirty() { return isSendQueueDirty; } void setDirty(bool dirty) { isSendQueueDirty = dirty; } From ab007ce0a5a3025fe704ce7ba8e77aa054c178ad Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Jan 2009 10:52:00 +0000 Subject: [PATCH 013/294] Checking in pcapextra.h and pcapextra.cpp that got left out in the last commit --- server/pcapextra.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++ server/pcapextra.h | 30 ++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..f3dd255 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,111 @@ +#include // memcpy() +#include // malloc(), free() +#include "pcapextra.h" + +/* NOTE: All code borrowed from WinPcap */ + +int pcap_setmode(pcap_t *p, int mode) +{ + // no STAT mode in libpcap, so just return 0 to indicate success + return 0; +} + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} + +u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) +{ + char* PacketBuff = queue->buffer; + int Size = queue->len; + + struct pcap_pkthdr *winpcap_hdr; + char* EndOfUserBuff = (char *)PacketBuff + Size; + int ret; + + // Start from the first packet + winpcap_hdr = (struct pcap_pkthdr*)PacketBuff; + + if((char*)winpcap_hdr + winpcap_hdr->caplen + sizeof(struct pcap_pkthdr) > + EndOfUserBuff ) + { + // Malformed buffer + return 0; + } + + while( true ){ + + if(winpcap_hdr->caplen ==0 || winpcap_hdr->caplen > 65536) + { + // Malformed header + return 0; + } + + // Send the packet + ret = pcap_sendpacket(p, + (unsigned char*)winpcap_hdr + sizeof(struct pcap_pkthdr), + winpcap_hdr->caplen); + + if(ret < 0){ + // Error sending the packet + return (char*)winpcap_hdr - (char*)PacketBuff; + } + + // Step to the next packet in the buffer + //(char*)winpcap_hdr += winpcap_hdr->caplen + sizeof(struct pcap_pkthdr); + winpcap_hdr = (struct pcap_pkthdr*) ((char*)winpcap_hdr + + winpcap_hdr->caplen + sizeof(struct pcap_pkthdr)); + + // Check if the end of the user buffer has been reached + if( (char*)winpcap_hdr >= EndOfUserBuff ) + { + return (char*)winpcap_hdr - (char*)PacketBuff; + } + } +} + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..25ae4bf --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,30 @@ +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#ifndef Q_OS_WIN32 + +#include "pcap.h" + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +int pcap_setmode(pcap_t *p, int mode); +#define MODE_STAT 1 + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); +u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync); + + +#endif + +#endif + From 9ac311f80fe8b52d1e3882ad7e5012115fc1944b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Jan 2009 11:37:35 +0000 Subject: [PATCH 014/294] Fixing the path that was unix specific to work for both unix and win32 --- client/ostinato.pro | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ostinato.pro b/client/ostinato.pro index 7e6c89e..572abb2 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -2,7 +2,9 @@ TEMPLATE = app CONFIG += qt debug QT += network INCLUDEPATH += "../rpc/" -LIBS += -lprotobuf -L"../rpc/" -lpbrpc +LIBS += -lprotobuf +win32:LIBS += -L"../rpc/debug" -lpbrpc +unix:LIBS += -L"../rpc" -lpbrpc RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ From 0c70668e564e285701ca60869b0458a781355d11 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 2 Feb 2009 10:08:57 +0000 Subject: [PATCH 015/294] Split the PCAP callback into 2 - one for Rx and one for Tx using pcap_setdirection() --- server/myservice.cpp | 248 ++++++++++++++++++++++++++++++++++--------- server/myservice.h | 30 ++++-- 2 files changed, 224 insertions(+), 54 deletions(-) diff --git a/server/myservice.cpp b/server/myservice.cpp index 5f69adc..97f54cd 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -482,33 +482,57 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) // ------------------ PortInfo -------------------- // PortInfo::PortInfo(uint id, pcap_if_t *dev) - : monitor(this) + : monitorRx(this), monitorTx(this) { char errbuf[PCAP_ERRBUF_SIZE]; this->dev = dev; - devHandle = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , + + /* + * Get 2 device handles - one for rx and one for tx. If we use only + * one handle for both rx and tx anythin that we tx using the single + * handle is not received back to us + */ + devHandleRx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , 1000 /*ms*/, errbuf); - if (devHandle == NULL) + if (devHandleRx == NULL) { qDebug("Error opening port %s: %s\n", - dev->name, pcap_geterr(devHandle)); + dev->name, pcap_geterr(devHandleRx)); } -#if 0 - if (pcap_setdirection(devHandle, PCAP_D_IN)<0) + if (pcap_setdirection(devHandleRx, PCAP_D_IN)<0) { qDebug("[%s] Error setting direction inbound only\n", dev->name); } -#endif /* By default, put the interface in statistics mode */ - if (pcap_setmode(devHandle, MODE_STAT)<0) + if (pcap_setmode(devHandleRx, MODE_STAT)<0) + { + qDebug("Error setting statistics mode.\n"); + } + + devHandleTx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , + 1000 /*ms*/, errbuf); + if (devHandleTx == NULL) + { + qDebug("Error opening port %s: %s\n", + dev->name, pcap_geterr(devHandleTx)); + } + + if (pcap_setdirection(devHandleTx, PCAP_D_OUT)<0) + { + qDebug("[%s] Error setting direction outbound only\n", dev->name); + } + + /* By default, put the interface in statistics mode */ + if (pcap_setmode(devHandleTx, MODE_STAT)<0) { qDebug("Error setting statistics mode.\n"); } d.mutable_port_id()->set_id(id); + #ifdef Q_OS_WIN32 d.set_name(QString("if%1").arg(id).toAscii().constData()); #else @@ -517,6 +541,8 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) else d.set_name(QString("if%1").arg(id).toAscii().constData()); #endif + d.set_name(d.name()+pcap_datalink_val_to_name(pcap_datalink(devHandleRx))); + if (dev->description) d.set_description(dev->description); d.set_is_enabled(true); // FIXME(MED):check @@ -534,7 +560,8 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) isSendQueueDirty=true; // Start the monitor thread - monitor.start(); + monitorRx.start(); + monitorTx.start(); } void PortInfo::update() @@ -612,11 +639,13 @@ void PortInfo::startTransmit() uint bytes, pkts; // TODO(HI): Stream Mode - one pass/continuous - bytes = pcap_sendqueue_transmit(devHandle, sendQueue, false); + // NOTE: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + bytes = pcap_sendqueue_transmit(devHandleRx, sendQueue, false); if (bytes < sendQueue->len) { qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", - id(), bytes, sendQueue->len, pcap_geterr(devHandle)); + id(), bytes, sendQueue->len, pcap_geterr(devHandleTx)); // parse sendqueue using 'bytes' to get actual pkts sent #if 0 @@ -649,11 +678,6 @@ void PortInfo::startTransmit() // together pcapExtra.txPkts += pkts; pcapExtra.txBytes += bytes; -#else - // We don't have a regular stats callback function here, so update - // Port TxStats directly here - stats.txPkts += pkts; - stats.txBytes += bytes; #endif } @@ -670,7 +694,27 @@ void PortInfo::resetStats() // ------------------ PortMonitor ------------------- // -PortInfo::PortMonitor::PortMonitor(PortInfo *port) +PortInfo::PortMonitorRx::PortMonitorRx(PortInfo *port) +{ + this->port = port; +#ifdef Q_OS_WIN32 + { + int sz = sizeof(PACKET_OID_DATA) + sizeof(quint64) + 4; + //oidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, + //sizeof(PACKET_OID_DATA) + sizeof(quint64) - 1); + oidData = (PPACKET_OID_DATA) malloc(sz); + if (oidData) + { + memset(oidData, 0, sz); + oidData->Length=sizeof(quint64); + } + else + qFatal("failed to alloc oidData"); + } +#endif +} + +PortInfo::PortMonitorTx::PortMonitorTx(PortInfo *port) { this->port = port; #ifdef Q_OS_WIN32 @@ -691,7 +735,60 @@ PortInfo::PortMonitor::PortMonitor(PortInfo *port) } #ifdef Q_OS_WIN32 -void PortInfo::PortMonitor::callback(u_char *state, +void PortInfo::PortMonitorRx::callbackRx(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + // This is the WinPcap Callback - which is a 'stats mode' callback + + uint usec; + PortInfo *port = (PortInfo*) state; + + quint64 pkts; + quint64 bytes; + + // Update RxStats and RxRates using PCAP data + pkts = *((quint64*)(pkt_data + 0)); + bytes = *((quint64*)(pkt_data + 8)); + + // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it + bytes -= pkts * ETH_FRAME_HDR_SIZE; + + usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTsRx.tv_usec); + port->stats.rxPps = (pkts * 1000000) / usec; + port->stats.rxBps = (bytes * 1000000) / usec; + + port->stats.rxPkts += pkts; + port->stats.rxBytes += bytes; + + // Store curr timestamp as last timestamp + port->lastTsRx.tv_sec = header->ts.tv_sec; + port->lastTsRx.tv_usec = header->ts.tv_usec; + +#if 0 + for (int i=0; i < 16; i++) + { + qDebug("%02x ", pkt_data[i]); + } + qDebug("{%d: %llu, %llu}\n", port->id(), + pkts, bytes); + qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts); + qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes); +#endif + + // Retreive NIC stats +#ifdef Q_OS_WIN32 + port->monitorRx.oidData->Oid = OID_GEN_RCV_OK; + if (PacketRequest(port->devHandleRx->adapter, 0, port->monitorRx.oidData)) + { + if (port->monitorRx.oidData->Length <= sizeof(port->stats.rxPktsNic)) + memcpy((void*)&port->stats.rxPktsNic, + (void*)port->monitorRx.oidData->Data, + port->monitorRx.oidData->Length); + } +#endif +} +void PortInfo::PortMonitorTx::callbackTx(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data) { // This is the WinPcap Callback - which is a 'stats mode' callback @@ -717,15 +814,25 @@ void PortInfo::PortMonitor::callback(u_char *state, port->stats.rxPkts += pkts; port->stats.rxBytes += bytes; - // Update TxStats from PcapExtra + // Since WinPCAP (due to NDIS limitation) cannot distinguish between + // rx/tx packets, pcap stats are not of much use - for the tx stats + // update from PcapExtra + + pkts = port->pcapExtra.txPkts - port->stats.txPkts; + bytes = port->pcapExtra.txBytes - port->stats.txBytes; + + // Use the pcap timestamp for rate calculation though + usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTs.tv_usec); + port->stats.txPps = (pkts * 1000000) / usec; + port->stats.txBps = (bytes * 1000000) / usec; + port->stats.txPkts = port->pcapExtra.txPkts; port->stats.txBytes = port->pcapExtra.txBytes; - //! \TODO TxRates - // Store curr timestamp as last timestamp - port->lastTs.tv_sec = header->ts.tv_sec; - port->lastTs.tv_usec = header->ts.tv_usec; + port->lastTsTx.tv_sec = header->ts.tv_sec; + port->lastTsTx.tv_usec = header->ts.tv_usec; #if 0 for (int i=0; i < 16; i++) @@ -740,26 +847,18 @@ void PortInfo::PortMonitor::callback(u_char *state, // Retreive NIC stats #ifdef Q_OS_WIN32 - port->monitor.oidData->Oid = OID_GEN_RCV_OK; - if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) + port->monitorTx.oidData->Oid = OID_GEN_XMIT_OK; + if (PacketRequest(port->devHandleTx->adapter, 0, port->monitorTx.oidData)) { - if (port->monitor.oidData->Length <= sizeof(port->stats.rxPktsNic)) - memcpy((void*)&port->stats.rxPktsNic, - (void*)port->monitor.oidData->Data, - port->monitor.oidData->Length); - } - port->monitor.oidData->Oid = OID_GEN_XMIT_OK; - if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData)) - { - if (port->monitor.oidData->Length <= sizeof(port->stats.txPktsNic)) + if (port->monitorTx.oidData->Length <= sizeof(port->stats.txPktsNic)) memcpy((void*)&port->stats.txPktsNic, - (void*)port->monitor.oidData->Data, - port->monitor.oidData->Length); + (void*)port->monitorTx.oidData->Data, + port->monitorTx.oidData->Length); } #endif } #else -void PortInfo::PortMonitor::callback(u_char *state, +void PortInfo::PortMonitorRx::callbackRx(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data) { // This is the LibPcap Callback - which is a 'capture mode' callback @@ -772,8 +871,8 @@ void PortInfo::PortMonitor::callback(u_char *state, quint64 bytes; // Update RxStats and RxRates using PCAP data - usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTs.tv_usec); + usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTsRx.tv_usec); // TODO(rate) #if 0 port->stats.rxPps = (pkts * 1000000) / usec; @@ -785,25 +884,78 @@ void PortInfo::PortMonitor::callback(u_char *state, port->stats.rxPkts++; port->stats.rxBytes += header->len; - // NOTE: Port TxStats are updated by Port Transmit Function itself - // since this callback is called only when a packet is received + // Store curr timestamp as last timestamp + port->lastTsRx.tv_sec = header->ts.tv_sec; + port->lastTsRx.tv_usec = header->ts.tv_usec; +} - //! \TODO TxRates +void PortInfo::PortMonitorTx::callbackTx(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + // This is the LibPcap Callback - which is a 'capture mode' callback + // This callback is called once for EVERY packet + + uint usec; + PortInfo *port = (PortInfo*) state; + + quint64 pkts; + quint64 bytes; + + // Update TxStats and TxRates using PCAP data + usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTsTx.tv_usec); + // TODO(rate) +#if 0 + port->stats.txPps = (pkts * 1000000) / usec; + port->stats.txBps = (bytes * 1000000) / usec; +#endif + + // Note: For a 'capture callback' PCAP reported bytes DOES NOT include + // ETH_FRAME_HDR_SIZE - so don't adjust for it + + port->stats.txPkts++; + port->stats.txBytes += header->len; // Store curr timestamp as last timestamp - port->lastTs.tv_sec = header->ts.tv_sec; - port->lastTs.tv_usec = header->ts.tv_usec; + port->lastTsTx.tv_sec = header->ts.tv_sec; + port->lastTsTx.tv_usec = header->ts.tv_usec; } #endif -void PortInfo::PortMonitor::run() +void PortInfo::PortMonitorRx::run() { int ret; - qDebug("before pcap_loop\n"); + qDebug("before pcap_loop rx \n"); /* Start the main loop */ - ret = pcap_loop(port->devHandle, -1, &PortInfo::PortMonitor::callback, - (u_char*) port); + ret = pcap_loop(port->devHandleRx, -1, + &PortInfo::PortMonitorRx::callbackRx, (u_char*) port); + + switch(ret) + { + case 0: + qDebug("Unexpected return from pcap_loop()\n"); + break; + case -1: + qDebug("Unsolicited (error) return from pcap_loop()\n"); + break; + case -2: + qDebug("Solicited return from pcap_loop()\n"); + break; + default: + qDebug("Unknown return value from pcap_loop()\n"); + } +} + +void PortInfo::PortMonitorTx::run() +{ + int ret; + + qDebug("before pcap_loopTx\n"); + + /* Start the main loop */ + ret = pcap_loop(port->devHandleTx, -1, + &PortInfo::PortMonitorTx::callbackTx, (u_char*) port); switch(ret) { diff --git a/server/myservice.h b/server/myservice.h index c0679fb..182c65f 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -53,7 +53,7 @@ class PortInfo { friend class MyService; - class PortMonitor: public QThread + class PortMonitorRx: public QThread { friend class PortInfo; @@ -62,8 +62,23 @@ class PortInfo PPACKET_OID_DATA oidData; #endif public: - PortMonitor(PortInfo *port); - static void callback(u_char *state, + PortMonitorRx(PortInfo *port); + static void callbackRx(u_char *state, + const struct pcap_pkthdr *header, const u_char *pkt_data); + void run(); + }; + + class PortMonitorTx: public QThread + { + friend class PortInfo; + + PortInfo *port; +#ifdef Q_OS_WIN32 + PPACKET_OID_DATA oidData; +#endif + public: + PortMonitorTx(PortInfo *port); + static void callbackTx(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data); void run(); }; @@ -104,15 +119,18 @@ class PortInfo }; pcap_if_t *dev; - pcap_t *devHandle; + pcap_t *devHandleRx; + pcap_t *devHandleTx; pcap_send_queue *sendQueue; bool isSendQueueDirty; PcapExtra pcapExtra; - PortMonitor monitor; + PortMonitorRx monitorRx; + PortMonitorTx monitorTx; struct PortStats epochStats; struct PortStats stats; - struct timeval lastTs; //! used for Rate Stats calculations + struct timeval lastTsRx; //! used for Rate Stats calculations + struct timeval lastTsTx; //! used for Rate Stats calculations /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ QList streamList; From 017cb75ae5d5711f55225c1219ac8bdb8249bce0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 12 Feb 2009 17:07:19 +0000 Subject: [PATCH 016/294] - Packet Transmission is now a separate thread to allow for event processing - Packet Transmission rate (IPG) is done - need to test. IBG is still pending - Per port there are two pcap_t handles - one for Rx and one for Tx: since PCAP does not capture loopback packets, transmission by OST happens on Rx Hdl so that they are recieved on the Tx Hdl - pcap_loop() changed to pcap_dispatch() to be able to work in PCAP/Linux - forgot exactly why :-) - Removed NIC stats - Implemented PortStatsFilterDialog ordering of ports - PortStatsWindow - Tooltip on Port column dispays stats limitations --- client/portstatsfilter.ui | 70 ++++------ client/portstatsfilterdialog.cpp | 43 +++++-- client/portstatsfilterdialog.h | 2 + client/portstatsmodel.cpp | 46 +++++-- client/portstatsmodel.h | 12 +- client/portstatswindow.cpp | 14 +- client/portstatswindow.ui | 32 ++--- server/myservice.cpp | 215 ++++++++++++++++++++++--------- server/myservice.h | 16 ++- 9 files changed, 294 insertions(+), 156 deletions(-) diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index e34fde2..a681c65 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -6,7 +6,7 @@ 0 0 588 - 298 + 320 @@ -17,9 +17,21 @@ + + false + + + false + + + QAbstractItemView::NoDragDrop + QAbstractItemView::ExtendedSelection + + QListView::Static + @@ -68,6 +80,18 @@ + + true + + + true + + + false + + + QAbstractItemView::InternalMove + QAbstractItemView::ExtendedSelection @@ -76,50 +100,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - U - - - - - - - D - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index af95303..6c97d85 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -4,13 +4,6 @@ PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) { setupUi(this); - // 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); @@ -37,6 +30,10 @@ QList PortStatsFilterDialog::getItemList(bool* ok, item = new QStandardItem(model->headerData(i, orientation).toString()); item->setData(i, PositionRole); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); if (initial.contains(i)) mSelected.appendRow(item); @@ -62,6 +59,36 @@ QList PortStatsFilterDialog::getItemList(bool* ok, } void PortStatsFilterDialog::on_tbSelectIn_clicked() +{ + QStandardItem *item; + while (lvUnselected->selectionModel()->selectedIndexes().size()) + { + item = mUnselected.takeItem(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mUnselected.removeRow(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row())) + mSelected.appendRow(item); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QStandardItem *item; + + while (lvSelected->selectionModel()->selectedIndexes().size()) + { + item = mSelected.takeItem(lvSelected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mSelected.removeRow(lvSelected->selectionModel()-> + selectedIndexes().at(0).row())) + { + mUnselected.appendRow(item); + mUnselected.sort(0); + } + } +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) { QStandardItem *item; @@ -70,7 +97,7 @@ void PortStatsFilterDialog::on_tbSelectIn_clicked() mSelected.appendRow(item); } -void PortStatsFilterDialog::on_tbSelectOut_clicked() +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) { QStandardItem *item; diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h index e845e0d..cabbf0c 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -27,6 +27,8 @@ private: private slots: void on_tbSelectIn_clicked(); void on_tbSelectOut_clicked(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); }; #endif diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index d554687..7676e42 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -105,12 +105,6 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const case e_STAT_FRAME_RECV_RATE: return stats.rx_pps(); - case e_STAT_FRAMES_RCVD_NIC: - return stats.rx_pkts_nic(); - - case e_STAT_FRAMES_SENT_NIC: - return stats.tx_pkts_nic(); - case e_STAT_BYTES_RCVD: return stats.rx_bytes(); @@ -123,12 +117,19 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const case e_STAT_BYTE_RECV_RATE: return stats.rx_bps(); +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + case e_STAT_BYTES_RCVD_NIC: return stats.rx_bytes_nic(); case e_STAT_BYTES_SENT_NIC: return stats.tx_bytes_nic(); - +#endif default: qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, index.row()); @@ -142,11 +143,39 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const { +#ifdef Q_OS_WIN32 + // TODO(MED): The limitations should be the server's not the client's! + // Ideally we shd enhance the protocol to convey limitation(s), if any, + // from server to client + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + return QString("Limitation(s)" + "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" + "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" + "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" + ); + } + else + return QVariant(); + } +#endif + if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) - return QString("Port %1").arg(section); + { + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); +#ifdef Q_OS_WIN32 + return QString("Port %1/%2 (*)").arg(portGroupIdx).arg(portIdx); +#else + return QString("Port %1/%2").arg(portGroupIdx).arg(portIdx); +#endif + } else return PortStatName.at(section); } @@ -212,7 +241,6 @@ void PortStatsModel::when_portListChanged() void PortStatsModel::on_portStatsUpdate(int port, void*stats) { - // FIXME(MED): update only the changed port not all QModelIndex topLeft = index(port, 0, QModelIndex()); QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 1825524..de1c585 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -9,14 +9,16 @@ typedef enum { e_STAT_FRAMES_SENT, e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_RECV_RATE, - e_STAT_FRAMES_RCVD_NIC, - e_STAT_FRAMES_SENT_NIC, e_STAT_BYTES_RCVD, e_STAT_BYTES_SENT, e_STAT_BYTE_SEND_RATE, e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, e_STAT_BYTES_RCVD_NIC, e_STAT_BYTES_SENT_NIC, +#endif e_STAT_MAX } PortStat; @@ -25,14 +27,16 @@ static QStringList PortStatName = (QStringList() << "Frames Sent" << "Frame Send Rate (fps)" << "Frame Receive Rate (fps)" - << "Frames Received (NIC)" - << "Frames Sent (NIC)" << "Bytes Received" << "Bytes Sent" << "Byte Send Rate (Bps)" << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" << "Bytes Received (NIC)" << "Bytes Sent (NIC)" +#endif ); class PortGroupList; diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 5f4ff6b..53be8b7 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -13,11 +13,11 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) this->pgl = pgl; model = pgl->getPortStatsModel(); tvPortStats->setModel(model); - tvPortStats->horizontalHeader()->setMovable(true); tvPortStats->verticalHeader()->setHighlightSections(false); tvPortStats->verticalHeader()->setDefaultSectionSize( tvPortStats->verticalHeader()->minimumSectionSize()); + } PortStatsWindow::~PortStatsWindow() @@ -109,7 +109,17 @@ void PortStatsWindow::on_tbFilter_clicked() newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); - if(ok) + if (ok) + { + // hide/show sections first ... for(int i = 0; i < model->columnCount(); i++) tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } } diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui index 4396ff6..a7fc8d5 100644 --- a/client/portstatswindow.ui +++ b/client/portstatswindow.ui @@ -15,9 +15,6 @@ - - Clear All - QFrame::StyledPanel @@ -28,7 +25,7 @@ - Clear All + Start Tx Starts transmit on selected port(s) @@ -44,7 +41,7 @@ - Clear All + Stop Tx Stops transmit on selected port(s) @@ -60,7 +57,7 @@ - Clear All + Clear Selected Port Stats Clears statistics of the selected port(s) @@ -73,7 +70,7 @@ - Clear All + Clear All Ports Stats Clears statistics of all ports @@ -86,7 +83,7 @@ - Clear All + Start Capture Captures packets on the selected port(s) @@ -102,7 +99,7 @@ - Clear All + Stop Capture End capture on selecteed port(s) @@ -118,7 +115,7 @@ - Clear All + View Capture Buffer View captured packets on selected port(s) @@ -133,9 +130,6 @@ - - Clear All - Qt::Vertical @@ -143,9 +137,6 @@ - - Clear All - Qt::Horizontal @@ -159,9 +150,6 @@ - - Clear All - Select which ports to view @@ -177,11 +165,7 @@ - - - Clear All - - + diff --git a/server/myservice.cpp b/server/myservice.cpp index 97f54cd..eef1610 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -4,7 +4,7 @@ #include #include -#ifdef Q_OS_WIN32 +#if 0 #include #include #endif @@ -482,7 +482,7 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) // ------------------ PortInfo -------------------- // PortInfo::PortInfo(uint id, pcap_if_t *dev) - : monitorRx(this), monitorTx(this) + : monitorRx(this), monitorTx(this), transmitter(this) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -501,10 +501,12 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) dev->name, pcap_geterr(devHandleRx)); } +#if 0 if (pcap_setdirection(devHandleRx, PCAP_D_IN)<0) { qDebug("[%s] Error setting direction inbound only\n", dev->name); } +#endif /* By default, put the interface in statistics mode */ if (pcap_setmode(devHandleRx, MODE_STAT)<0) @@ -520,10 +522,12 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) dev->name, pcap_geterr(devHandleTx)); } +#if 0 if (pcap_setdirection(devHandleTx, PCAP_D_OUT)<0) { qDebug("[%s] Error setting direction outbound only\n", dev->name); } +#endif /* By default, put the interface in statistics mode */ if (pcap_setmode(devHandleTx, MODE_STAT)<0) @@ -534,14 +538,15 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) d.mutable_port_id()->set_id(id); #ifdef Q_OS_WIN32 - d.set_name(QString("if%1").arg(id).toAscii().constData()); + d.set_name(QString("if%1 ").arg(id).toAscii().constData()); #else if (dev->name) d.set_name(dev->name); else - d.set_name(QString("if%1").arg(id).toAscii().constData()); + d.set_name(QString("if%1 ").arg(id).toAscii().constData()); #endif - d.set_name(d.name()+pcap_datalink_val_to_name(pcap_datalink(devHandleRx))); + d.set_name(d.name()+"{"+ + pcap_datalink_val_to_name(pcap_datalink(devHandleRx))+"}"); if (dev->description) d.set_description(dev->description); @@ -586,6 +591,7 @@ void PortInfo::update() if (streamList[i].d.core().is_enabled()) { int numPackets, numBursts; + long ipg; switch (streamList[i].d.control().unit()) { @@ -596,6 +602,8 @@ void PortInfo::update() case OstProto::StreamControl::e_su_packets: numBursts = 1; numPackets = streamList[i].d.control().num_packets(); + ipg = 1000000/streamList[i].d.control().packets_per_sec(); + qDebug("ipg = %ld\n", ipg); break; default: qWarning("Unhandled stream control unit %d", @@ -603,9 +611,11 @@ void PortInfo::update() continue; } - + pktHdr.ts.tv_sec = 0; + pktHdr.ts.tv_usec = 0; for (int j = 0; j < numBursts; j++) { + // FIXME(HI): IBG rate (bursts_per_sec) for (int k = 0; k < numPackets; k++) { int len; @@ -615,7 +625,12 @@ void PortInfo::update() if (len > 0) { pktHdr.caplen = pktHdr.len = len; - pktHdr.ts.tv_sec = pktHdr.ts.tv_usec = 0; // FIXME(HI) + pktHdr.ts.tv_usec += ipg; + if (pktHdr.ts.tv_usec > 1000000) + { + pktHdr.ts.tv_sec++; + pktHdr.ts.tv_usec -= 1000000; + } if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, (u_char*) pktBuf)) @@ -636,49 +651,7 @@ void PortInfo::update() void PortInfo::startTransmit() { - uint bytes, pkts; - - // TODO(HI): Stream Mode - one pass/continuous - // NOTE: Transmit on the Rx Handle so that we can receive it back - // on the Tx Handle to do stats - bytes = pcap_sendqueue_transmit(devHandleRx, sendQueue, false); - if (bytes < sendQueue->len) - { - qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", - id(), bytes, sendQueue->len, pcap_geterr(devHandleTx)); - - // parse sendqueue using 'bytes' to get actual pkts sent -#if 0 - // FIXME(LOW): Get this working - pkts = qUpperBound(pcapExtra.sendQueueCumLen, bytes); -#else - for (int i = 0; i < pcapExtra.sendQueueCumLen.size(); i++) - { - if (pcapExtra.sendQueueCumLen.at(i) > bytes) - { - pkts = i; - break; - } - } -#endif - } - else - { - qDebug("port %d: sent (%d/%d) bytes\n", id(), bytes, sendQueue->len); - pkts = pcapExtra.sendQueueCumLen.size(); - } - - // pcap_sendqueue_transmit() returned 'bytes' includes size of pcap_pkthdr - // - adjust for it - if (bytes) - bytes -= pkts * sizeof(pcap_pkthdr); -#ifdef Q_OS_WIN32 - // Update pcapExtra counters - port TxStats will be updated in the - // 'stats callback' function so that both Rx and Tx stats are updated - // together - pcapExtra.txPkts += pkts; - pcapExtra.txBytes += bytes; -#endif + transmitter.start(); } void PortInfo::stopTransmit() @@ -750,6 +723,11 @@ void PortInfo::PortMonitorRx::callbackRx(u_char *state, pkts = *((quint64*)(pkt_data + 0)); bytes = *((quint64*)(pkt_data + 8)); +#if 0 + if (port->id() == 2) + qDebug("# %llu", pkts); +#endif + // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it bytes -= pkts * ETH_FRAME_HDR_SIZE; @@ -777,7 +755,7 @@ void PortInfo::PortMonitorRx::callbackRx(u_char *state, #endif // Retreive NIC stats -#ifdef Q_OS_WIN32 +#if 0 port->monitorRx.oidData->Oid = OID_GEN_RCV_OK; if (PacketRequest(port->devHandleRx->adapter, 0, port->monitorRx.oidData)) { @@ -799,20 +777,28 @@ void PortInfo::PortMonitorTx::callbackTx(u_char *state, quint64 pkts; quint64 bytes; + +#if 0 // Update RxStats and RxRates using PCAP data pkts = *((quint64*)(pkt_data + 0)); bytes = *((quint64*)(pkt_data + 8)); +#if 0 + if (port->id() == 2) + qDebug("@ %llu", pkts); +#endif + // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it bytes -= pkts * ETH_FRAME_HDR_SIZE; - usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTs.tv_usec); - port->stats.rxPps = (pkts * 1000000) / usec; - port->stats.rxBps = (bytes * 1000000) / usec; + usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTsTx.tv_usec); + port->stats.txPps = (pkts * 1000000) / usec; + port->stats.txBps = (bytes * 1000000) / usec; - port->stats.rxPkts += pkts; - port->stats.rxBytes += bytes; + port->stats.txPkts += pkts; + port->stats.txBytes += bytes; +#endif // Since WinPCAP (due to NDIS limitation) cannot distinguish between // rx/tx packets, pcap stats are not of much use - for the tx stats @@ -822,8 +808,8 @@ void PortInfo::PortMonitorTx::callbackTx(u_char *state, bytes = port->pcapExtra.txBytes - port->stats.txBytes; // Use the pcap timestamp for rate calculation though - usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTs.tv_usec); + usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + + (header->ts.tv_usec - port->lastTsTx.tv_usec); port->stats.txPps = (pkts * 1000000) / usec; port->stats.txBps = (bytes * 1000000) / usec; @@ -846,7 +832,7 @@ void PortInfo::PortMonitorTx::callbackTx(u_char *state, #endif // Retreive NIC stats -#ifdef Q_OS_WIN32 +#if 0 port->monitorTx.oidData->Oid = OID_GEN_XMIT_OK; if (PacketRequest(port->devHandleTx->adapter, 0, port->monitorTx.oidData)) { @@ -926,7 +912,7 @@ void PortInfo::PortMonitorRx::run() int ret; qDebug("before pcap_loop rx \n"); - +#if 1 /* Start the main loop */ ret = pcap_loop(port->devHandleRx, -1, &PortInfo::PortMonitorRx::callbackRx, (u_char*) port); @@ -945,6 +931,28 @@ void PortInfo::PortMonitorRx::run() default: qDebug("Unknown return value from pcap_loop()\n"); } +#else + while (1) + { + /* Start the main loop */ + ret = pcap_dispatch(port->devHandleRx, -1, + &PortInfo::PortMonitorRx::callbackRx, (u_char*) port); + + switch(ret) + { + case -1: + qDebug("Unsolicited (error) return from pcap_loop() %s\n", + pcap_geterr(port->devHandleRx)); + break; + case -2: + qDebug("Solicited return from pcap_loop()\n"); + break; + default: + //qDebug("%d pkts rcvd\n", ret); + break; + } + } +#endif } void PortInfo::PortMonitorTx::run() @@ -952,7 +960,7 @@ void PortInfo::PortMonitorTx::run() int ret; qDebug("before pcap_loopTx\n"); - +#if 1 /* Start the main loop */ ret = pcap_loop(port->devHandleTx, -1, &PortInfo::PortMonitorTx::callbackTx, (u_char*) port); @@ -971,6 +979,84 @@ void PortInfo::PortMonitorTx::run() default: qDebug("Unknown return value from pcap_loop()\n"); } +#else + while (1) + { + /* Start the main loop */ + ret = pcap_dispatch(port->devHandleTx, -1, + &PortInfo::PortMonitorTx::callbackTx, (u_char*) port); + + switch(ret) + { + case -1: + qDebug("Unsolicited (error) return from pcap_loop() %s\n", + pcap_geterr(port->devHandleTx)); + break; + case -2: + qDebug("Solicited return from pcap_loop()\n"); + break; + default: + //qDebug("%d pkts rcvd\n", ret); + break; + } + } +#endif +} + +/*--------------- PortTransmitter ---------------*/ + +PortInfo::PortTransmitter::PortTransmitter(PortInfo *port) +{ + this->port = port; +} + +void PortInfo::PortTransmitter::run() +{ + uint bytes, pkts; + + // TODO(HI): Stream Mode - one pass/continuous + // NOTE: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + bytes = pcap_sendqueue_transmit(port->devHandleRx, port->sendQueue, true); + if (bytes < port->sendQueue->len) + { + qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", + port->id(), bytes, port->sendQueue->len, + pcap_geterr(port->devHandleTx)); + + // parse sendqueue using 'bytes' to get actual pkts sent +#if 0 + // FIXME(LOW): Get this working + pkts = qUpperBound(pcapExtra.sendQueueCumLen, bytes); +#else + for (int i = 0; i < port->pcapExtra.sendQueueCumLen.size(); i++) + { + if (port->pcapExtra.sendQueueCumLen.at(i) > bytes) + { + pkts = i; + break; + } + } +#endif + } + else + { + qDebug("port %d: sent (%d/%d) bytes\n", port->id(), bytes, + port->sendQueue->len); + pkts = port->pcapExtra.sendQueueCumLen.size(); + } + + // pcap_sendqueue_transmit() returned 'bytes' includes size of pcap_pkthdr + // - adjust for it + if (bytes) + bytes -= pkts * sizeof(pcap_pkthdr); +#ifdef Q_OS_WIN32 + // Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + port->pcapExtra.txPkts += pkts; + port->pcapExtra.txBytes += bytes; +#endif } /*--------------- MyService ---------------*/ @@ -1369,6 +1455,11 @@ const ::OstProto::PortIdList* request, s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); + if (portidx == 2) + { + qDebug("<%llu", portInfo[portidx]->epochStats.rxPkts); + qDebug(">%llu", portInfo[portidx]->stats.rxPkts); + } s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - portInfo[portidx]->epochStats.rxPkts); s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - diff --git a/server/myservice.h b/server/myservice.h index 182c65f..6155cac 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -82,6 +82,17 @@ class PortInfo const struct pcap_pkthdr *header, const u_char *pkt_data); void run(); }; + + class PortTransmitter: public QThread + { + friend class PortInfo; + + PortInfo *port; + + public: + PortTransmitter(PortInfo *port); + void run(); + }; OstProto::Port d; @@ -111,7 +122,7 @@ class PortInfo //! Used to track num of packets (and their sizes) in the // send queue. Also used to find out actual num of pkts sent // in case of partial send in pcap_sendqueue_transmit() - QList sendQueueCumLen; + QList sendQueueCumLen; //! PCAP doesn't do any tx stats quint64 txPkts; @@ -121,11 +132,12 @@ class PortInfo pcap_if_t *dev; pcap_t *devHandleRx; pcap_t *devHandleTx; - pcap_send_queue *sendQueue; + pcap_send_queue* sendQueue; bool isSendQueueDirty; PcapExtra pcapExtra; PortMonitorRx monitorRx; PortMonitorTx monitorTx; + PortTransmitter transmitter; struct PortStats epochStats; struct PortStats stats; From f13b0915d57f5347aadb5fc4f12f3b053374ace8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 22 Feb 2009 07:53:14 +0000 Subject: [PATCH 017/294] Packet Transmit Changes - not using pcap_sendqueue_transmit() any longer --- client/portstatsmodel.cpp | 2 +- client/portstatswindow.cpp | 2 +- common/protocol.proto | 4 +- server/drone.pro | 2 +- server/myservice.cpp | 126 +++++++++++++++++++++---------------- server/myservice.h | 12 ++-- server/pcapextra.cpp | 47 +++++++++++++- server/pcapextra.h | 28 ++++++++- 8 files changed, 154 insertions(+), 69 deletions(-) diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 7676e42..bcc0f54 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -12,7 +12,7 @@ PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); - timer->start(2000); + timer->start(1000); } int PortStatsModel::rowCount(const QModelIndex &parent) const diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 53be8b7..d1340a6 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -54,7 +54,7 @@ void PortStatsWindow::on_tbStopTransmit_clicked() for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). - startTx(&pgpl[i].portList); + stopTx(&pgpl[i].portList); } } diff --git a/common/protocol.proto b/common/protocol.proto index 04c7a70..7038517 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -227,8 +227,8 @@ message StreamControl { optional uint32 num_bursts = 4 [default = 1]; optional uint32 packets_per_burst = 5 [default = 10]; optional NextWhat next = 6 [default = e_nw_goto_next]; - optional uint32 packets_per_sec = 7; - optional uint32 bursts_per_sec = 8; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; // TODO: Gaps? diff --git a/server/drone.pro b/server/drone.pro index 315a0e2..15d69b1 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -12,6 +12,6 @@ FORMS += drone.ui SOURCES += drone_main.cpp drone.cpp SOURCES += myservice.cpp -unix:SOURCES += pcapextra.cpp +SOURCES += pcapextra.cpp SOURCES += "..\common\protocol.pb.cc" diff --git a/server/myservice.cpp b/server/myservice.cpp index eef1610..dd19090 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -558,8 +558,7 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) resetStats(); // We'll create sendqueue later when required - sendQueue = NULL; - pcapExtra.sendQueueCumLen.clear(); + sendQueueList.clear(); pcapExtra.txPkts = 0; pcapExtra.txBytes = 0; isSendQueueDirty=true; @@ -571,17 +570,21 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) void PortInfo::update() { - uchar pktBuf[2000]; - pcap_pkthdr pktHdr; + uchar pktBuf[2000]; + pcap_pkthdr pktHdr; + ost_pcap_send_queue sendQ; qDebug("In %s", __FUNCTION__); - if (sendQueue) - pcap_sendqueue_destroy(sendQueue); + if (sendQueueList.size()) + { + foreach(sendQ, sendQueueList) + pcap_sendqueue_destroy(sendQ.sendQueue); + } // TODO(LOW): calculate sendqueue size - sendQueue = pcap_sendqueue_alloc(1*MB); - pcapExtra.sendQueueCumLen.clear(); + sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); + sendQ.sendQueueCumLen.clear(); // First sort the streams by ordinalValue qSort(streamList); @@ -590,32 +593,36 @@ void PortInfo::update() { if (streamList[i].d.core().is_enabled()) { - int numPackets, numBursts; - long ipg; + long numPackets, numBursts; + long ibg, ipg; switch (streamList[i].d.control().unit()) { case OstProto::StreamControl::e_su_bursts: numBursts = streamList[i].d.control().num_bursts(); numPackets = streamList[i].d.control().packets_per_burst(); + ibg = 1000000/streamList[i].d.control().bursts_per_sec(); + ipg = 0; break; case OstProto::StreamControl::e_su_packets: numBursts = 1; numPackets = streamList[i].d.control().num_packets(); + ibg = 0; ipg = 1000000/streamList[i].d.control().packets_per_sec(); - qDebug("ipg = %ld\n", ipg); break; default: qWarning("Unhandled stream control unit %d", streamList[i].d.control().unit()); continue; } + qDebug("numBursts = %ld, numPackets = %ld\n", + numBursts, numPackets); + qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); pktHdr.ts.tv_sec = 0; pktHdr.ts.tv_usec = 0; for (int j = 0; j < numBursts; j++) { - // FIXME(HI): IBG rate (bursts_per_sec) for (int k = 0; k < numPackets; k++) { int len; @@ -632,20 +639,45 @@ void PortInfo::update() pktHdr.ts.tv_usec -= 1000000; } - if (-1 == pcap_sendqueue_queue(sendQueue, &pktHdr, + // Not enough space? Alloc another one! + if ((sendQ.sendQueue->len + len + sizeof(pcap_pkthdr)) + > sendQ.sendQueue->maxlen) + { + sendQueueList.append(sendQ); + + // TODO(LOW): calculate sendqueue size + sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); + sendQ.sendQueueCumLen.clear(); + +#if 0 + pktHdr.ts.tv_sec = 0; + pktHdr.ts.tv_usec = 0; +#endif + } + + if (-1 == pcap_sendqueue_queue(sendQ.sendQueue, &pktHdr, (u_char*) pktBuf)) { qDebug("[port %d] sendqueue_queue() failed for " "streamidx %d\n", id(), i); } else - pcapExtra.sendQueueCumLen.append(sendQueue->len); + sendQ.sendQueueCumLen.append(sendQ.sendQueue->len); } } + pktHdr.ts.tv_usec += ibg; + if (pktHdr.ts.tv_usec > 1000000) + { + pktHdr.ts.tv_sec++; + pktHdr.ts.tv_usec -= 1000000; + } } } } + // The last alloc'ed sendQ appended here + sendQueueList.append(sendQ); + isSendQueueDirty = false; } @@ -656,6 +688,7 @@ void PortInfo::startTransmit() void PortInfo::stopTransmit() { + transmitter.stop(); } void PortInfo::resetStats() @@ -1012,53 +1045,33 @@ PortInfo::PortTransmitter::PortTransmitter(PortInfo *port) void PortInfo::PortTransmitter::run() { - uint bytes, pkts; - // TODO(HI): Stream Mode - one pass/continuous - // NOTE: Transmit on the Rx Handle so that we can receive it back + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back // on the Tx Handle to do stats - bytes = pcap_sendqueue_transmit(port->devHandleRx, port->sendQueue, true); - if (bytes < port->sendQueue->len) - { - qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent", - port->id(), bytes, port->sendQueue->len, - pcap_geterr(port->devHandleTx)); - - // parse sendqueue using 'bytes' to get actual pkts sent -#if 0 - // FIXME(LOW): Get this working - pkts = qUpperBound(pcapExtra.sendQueueCumLen, bytes); -#else - for (int i = 0; i < port->pcapExtra.sendQueueCumLen.size(); i++) - { - if (port->pcapExtra.sendQueueCumLen.at(i) > bytes) - { - pkts = i; - break; - } - } -#endif - } - else - { - qDebug("port %d: sent (%d/%d) bytes\n", port->id(), bytes, - port->sendQueue->len); - pkts = port->pcapExtra.sendQueueCumLen.size(); - } - - // pcap_sendqueue_transmit() returned 'bytes' includes size of pcap_pkthdr - // - adjust for it - if (bytes) - bytes -= pkts * sizeof(pcap_pkthdr); -#ifdef Q_OS_WIN32 - // Update pcapExtra counters - port TxStats will be updated in the + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the // 'stats callback' function so that both Rx and Tx stats are updated // together - port->pcapExtra.txPkts += pkts; - port->pcapExtra.txBytes += bytes; -#endif + + m_stop = 0; + ost_pcap_sendqueue_list_transmit(port->devHandleRx, port->sendQueueList, + true, &m_stop, &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, + QThread::usleep); + m_stop = 0; } +void PortInfo::PortTransmitter::stop() +{ + m_stop = 1; +} + + /*--------------- MyService ---------------*/ int MyService::getStreamIndex(unsigned int portIdx, @@ -1455,11 +1468,14 @@ const ::OstProto::PortIdList* request, s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); +#if 0 if (portidx == 2) { qDebug("<%llu", portInfo[portidx]->epochStats.rxPkts); qDebug(">%llu", portInfo[portidx]->stats.rxPkts); } +#endif + s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - portInfo[portidx]->epochStats.rxPkts); s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - diff --git a/server/myservice.h b/server/myservice.h index 6155cac..7f1cb88 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -87,11 +87,13 @@ class PortInfo { friend class PortInfo; - PortInfo *port; + PortInfo *port; + int m_stop; public: PortTransmitter(PortInfo *port); void run(); + void stop(); }; OstProto::Port d; @@ -114,25 +116,23 @@ class PortInfo }; //! \todo Need lock for stats access/update + //! Stuff we need to maintain since PCAP doesn't as of now. As and when // PCAP supports it, we'll remove from here struct PcapExtra { - //! Used to track num of packets (and their sizes) in the - // send queue. Also used to find out actual num of pkts sent - // in case of partial send in pcap_sendqueue_transmit() - QList sendQueueCumLen; //! PCAP doesn't do any tx stats quint64 txPkts; quint64 txBytes; + }; pcap_if_t *dev; pcap_t *devHandleRx; pcap_t *devHandleTx; - pcap_send_queue* sendQueue; + QList sendQueueList; bool isSendQueueDirty; PcapExtra pcapExtra; PortMonitorRx monitorRx; diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index f3dd255..78d57e0 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -4,6 +4,7 @@ /* NOTE: All code borrowed from WinPcap */ +#ifndef Q_OS_WIN32 int pcap_setmode(pcap_t *p, int mode) { // no STAT mode in libpcap, so just return 0 to indicate success @@ -58,13 +59,36 @@ int pcap_sendqueue_queue (pcap_send_queue *queue, return 0; } +#endif -u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) +u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, + QList sendQueueList, int sync, + int *p_stop, quint64* p_pkts, quint64* p_bytes, + void (*pf_usleep)(ulong)) +{ + uint ret = 0; + + foreach(ost_pcap_send_queue sq, sendQueueList) + { + ret += ost_pcap_sendqueue_transmit(p, sq.sendQueue, sync, + p_stop, p_pkts, p_bytes, pf_usleep); + + // TODO(HI): Timing between subsequent sendQueues + } + + return ret; +} + +u_int ost_pcap_sendqueue_transmit(pcap_t *p, + pcap_send_queue *queue, int sync, + int *p_stop, quint64* p_pkts, quint64* p_bytes, + void (*pf_usleep)(ulong)) { char* PacketBuff = queue->buffer; int Size = queue->len; struct pcap_pkthdr *winpcap_hdr; + struct timeval ts; char* EndOfUserBuff = (char *)PacketBuff + Size; int ret; @@ -78,8 +102,14 @@ u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) return 0; } + if (sync) + ts = winpcap_hdr->ts; + while( true ){ + if (*p_stop) + return (char*)winpcap_hdr - (char*)PacketBuff; + if(winpcap_hdr->caplen ==0 || winpcap_hdr->caplen > 65536) { // Malformed header @@ -96,6 +126,9 @@ u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) return (char*)winpcap_hdr - (char*)PacketBuff; } + if (p_pkts) (*p_pkts)++; + if (p_bytes) (*p_bytes) += winpcap_hdr->caplen; + // Step to the next packet in the buffer //(char*)winpcap_hdr += winpcap_hdr->caplen + sizeof(struct pcap_pkthdr); winpcap_hdr = (struct pcap_pkthdr*) ((char*)winpcap_hdr + @@ -106,6 +139,18 @@ u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) { return (char*)winpcap_hdr - (char*)PacketBuff; } + + if (sync) + { + long usec = (winpcap_hdr->ts.tv_sec-ts.tv_sec)*1000000 + + (winpcap_hdr->ts.tv_usec - ts.tv_usec); + + if (usec) + { + (*pf_usleep)(usec); + ts = winpcap_hdr->ts; + } + } } } diff --git a/server/pcapextra.h b/server/pcapextra.h index 25ae4bf..3d2f8f4 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -1,10 +1,35 @@ #ifndef _PCAP_EXTRA_H #define _PCAP_EXTRA_H -#ifndef Q_OS_WIN32 +#include +#include #include "pcap.h" +struct ost_pcap_send_queue +{ + pcap_send_queue *sendQueue; + //! Used to track num of packets (and their sizes) in the + // send queue. Also used to find out actual num of pkts sent + // in case of partial send in pcap_sendqueue_transmit() + QList sendQueueCumLen; +}; + +// Common for all OS - *nix or Win32 +u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, + QList sendQueueList, int sync, + int *p_stop, quint64* p_pkts, quint64* p_bytes, + void (*pf_usleep)(ulong)); + +u_int ost_pcap_sendqueue_transmit (pcap_t *p, + pcap_send_queue *queue, int sync, + int *p_stop, quint64* p_pkts, quint64* p_bytes, + void (*pf_usleep)(ulong)); + +#ifndef Q_OS_WIN32 +// Only for non Win32 + + #define PCAP_OPENFLAG_PROMISCUOUS 1 struct pcap_send_queue @@ -21,7 +46,6 @@ pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); void pcap_sendqueue_destroy (pcap_send_queue *queue); int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); -u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync); #endif From 53bcc077dae3a220d2ba8f7a524d549e6ef261e8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 10 Mar 2009 16:48:03 +0000 Subject: [PATCH 018/294] - Implemented the "Stop" and "Goto Stream" per stream "nextWhat" options (Goto can only go to first stream for now - not any arbitrary stream) - StreamListView now has a delegate to display a combobox for "nextWhat" and a checkbox for "status" - StreamListView now has reasonable default widths for its columns --- client/ostinato.pro | 2 + client/portswindow.cpp | 9 +- client/streamconfigdialog.cpp | 8 +- client/streamconfigdialog.ui | 3 + client/streamlistdelegate.cpp | 175 ++++++++++++++++++++++++++++++++++ client/streamlistdelegate.h | 29 ++++++ client/streammodel.cpp | 65 ++++++++----- client/streammodel.h | 22 +++-- server/myservice.cpp | 55 +++++++++-- server/myservice.h | 1 + server/pcapextra.cpp | 18 +++- server/pcapextra.h | 2 +- 12 files changed, 342 insertions(+), 47 deletions(-) create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h diff --git a/client/ostinato.pro b/client/ostinato.pro index 572abb2..ec44eb0 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -20,6 +20,7 @@ HEADERS += \ portstatswindow.h \ portswindow.h \ streamconfigdialog.h \ + streamlistdelegate.h \ streammodel.h FORMS += \ @@ -45,6 +46,7 @@ SOURCES += \ portstatswindow.cpp \ portswindow.cpp \ streamconfigdialog.cpp \ + streamlistdelegate.cpp \ streammodel.cpp # Protocol Buffer Sources diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 27dec05..1789e5e 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,18 +1,20 @@ #include "portswindow.h" +#include "streamlistdelegate.h" #include "streamconfigdialog.h" #include #include PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) { + StreamListDelegate *delegate = new StreamListDelegate; //slm = new StreamListModel(); //plm = new PortGroupList(); plm = pgl; setupUi(this); - tvStreamList->horizontalHeader()->resizeSections( - QHeaderView::ResizeToContents); + tvStreamList->setItemDelegate(delegate); + tvStreamList->verticalHeader()->setDefaultSectionSize( tvStreamList->verticalHeader()->minimumSectionSize()); @@ -53,6 +55,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); #endif + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + // Initially we don't have any ports/streams - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); when_streamView_currentChanged(QModelIndex(), QModelIndex()); diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 7ee59f7..e7f1f0b 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -173,7 +173,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, pbPrev->setDisabled(true); pbNext->setDisabled(true); //! \todo Support Goto Stream Id - rbActionGotoStream->setDisabled(true); + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); } @@ -824,10 +825,11 @@ void StreamConfigDialog::LoadCurrentStream() leNumPackets->setText(QString().setNum(pStream->numPackets())); leNumBursts->setText(QString().setNum(pStream->numBursts())); - lePacketsPerBurst->setText(QString().setNum( - pStream->burstSize())); + lePacketsPerBurst->setText(QString().setNum(pStream->burstSize())); lePacketsPerSec->setText(QString().setNum(pStream->packetRate())); leBurstsPerSec->setText(QString().setNum(pStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); } } diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index b94e107..178e966 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -1915,6 +1915,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
+ + false + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f67b99e --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a4e8d35 --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,29 @@ +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 32dce31..6680f58 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -1,3 +1,4 @@ +#include "stream.h" #include "streammodel.h" #include "portgrouplist.h" #include "qicon.h" @@ -38,17 +39,19 @@ Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const flags |= Qt::ItemIsEditable; break; case StreamStatus: -#if 0 - flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable; -#endif + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: flags |= Qt::ItemIsEditable; break; default: + //qFatal("Missed case in switch!"); break; } return flags; } + QVariant StreamModel::data(const QModelIndex &index, int role) const { // Check for a valid index @@ -72,10 +75,6 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole) return QIcon(":/icons/stream_edit.png"); -#if 0 - else if ((role == Qt::DisplayRole)) - return QString("EDIT"); -#endif else return QVariant(); break; @@ -90,12 +89,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } case StreamStatus: { - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->streamByIndex(index.row()).isEnabled(); - else - return QVariant(); - #if 0 - if ((role == Qt::CheckStateRole) || (role == Qt::EditRole)) + if ((role == Qt::CheckStateRole)) { if (mCurrentPort->streamByIndex(index.row()).isEnabled()) return Qt::Checked; @@ -104,11 +98,23 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } else return QVariant(); - #endif + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row()).nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + break; } default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); + qFatal("-------------UNHANDLED STREAM FIELD----------------"); } return QVariant(); @@ -119,24 +125,35 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r if (mCurrentPort == NULL) return false; - if (index.isValid() && role == Qt::EditRole) + if (index.isValid()) { - // Edit Supported Fields switch (index.column()) { + // Edit Supported Fields case StreamName: mCurrentPort->streamByIndex(index.row()).setName(value.toString()); + emit(dataChanged(index, index)); return true; - break; + case StreamStatus: mCurrentPort->streamByIndex(index.row()).setIsEnabled(value.toBool()); + emit(dataChanged(index, index)); return true; - break; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row()).setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; // Edit Not Supported Fields case StreamIcon: return false; - break; // Unhandled Stream Field default: @@ -144,6 +161,7 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r break; } } + return false; } @@ -157,13 +175,16 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r switch(section) { case StreamIcon: - return QString("Icon"); + return QString(""); break; case StreamName: return QString("Name"); break; case StreamStatus: - return QString("Enabled"); + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); break; default: qDebug("-------------UNHANDLED STREAM FIELD----------------"); diff --git a/client/streammodel.h b/client/streammodel.h index 697517d..95af6fe 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -1,6 +1,7 @@ #ifndef _STREAM_MODEL_H #define _STREAM_MODEL_H +#include #include #include "port.h" @@ -10,14 +11,6 @@ class StreamModel : public QAbstractTableModel { Q_OBJECT - enum StreamFields { - StreamIcon = 0, - StreamName, - StreamStatus, - - StreamMaxFields - }; - Port *mCurrentPort; PortGroupList *pgl; @@ -43,6 +36,19 @@ class StreamModel : public QAbstractTableModel { return &mCurrentPort->mStreams; } #endif + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + public slots: void setCurrentPortIndex(const QModelIndex ¤t); diff --git a/server/myservice.cpp b/server/myservice.cpp index dd19090..085f6f5 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -559,6 +559,7 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) // We'll create sendqueue later when required sendQueueList.clear(); + returnToQIdx = -1; pcapExtra.txPkts = 0; pcapExtra.txBytes = 0; isSendQueueDirty=true; @@ -581,6 +582,8 @@ void PortInfo::update() foreach(sendQ, sendQueueList) pcap_sendqueue_destroy(sendQ.sendQueue); } + sendQueueList.clear(); + returnToQIdx = -1; // TODO(LOW): calculate sendqueue size sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); @@ -589,8 +592,11 @@ void PortInfo::update() // First sort the streams by ordinalValue qSort(streamList); + pktHdr.ts.tv_sec = 0; + pktHdr.ts.tv_usec = 0; for (int i = 0; i < streamList.size(); i++) { +//_restart: if (streamList[i].d.core().is_enabled()) { long numPackets, numBursts; @@ -619,8 +625,6 @@ void PortInfo::update() numBursts, numPackets); qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); - pktHdr.ts.tv_sec = 0; - pktHdr.ts.tv_usec = 0; for (int j = 0; j < numBursts; j++) { for (int k = 0; k < numPackets; k++) @@ -655,6 +659,9 @@ void PortInfo::update() #endif } + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, pktHdr.ts.tv_sec, pktHdr.ts.tv_usec); + if (-1 == pcap_sendqueue_queue(sendQ.sendQueue, &pktHdr, (u_char*) pktBuf)) { @@ -664,17 +671,48 @@ void PortInfo::update() else sendQ.sendQueueCumLen.append(sendQ.sendQueue->len); } - } + } // for (numPackets) pktHdr.ts.tv_usec += ibg; if (pktHdr.ts.tv_usec > 1000000) { pktHdr.ts.tv_sec++; pktHdr.ts.tv_usec -= 1000000; } - } - } - } + } // for (numBursts) + switch(streamList[i].d.control().next()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + // TODO(MED): define and use + // streamList[i].d.control().goto_stream_id(); + + // TODO(MED): assumes goto Id is less than current!!!! + // To support goto to any id, do + // if goto_id > curr_id then + // i = goto_id; + // goto restart; + // else + // returnToQIdx = 0; + + returnToQIdx=0; + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList[i].d.control().next() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: // The last alloc'ed sendQ appended here sendQueueList.append(sendQ); @@ -1061,8 +1099,9 @@ void PortInfo::PortTransmitter::run() m_stop = 0; ost_pcap_sendqueue_list_transmit(port->devHandleRx, port->sendQueueList, - true, &m_stop, &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, - QThread::usleep); + port->returnToQIdx, true, &m_stop, + &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, + QThread::usleep); m_stop = 0; } diff --git a/server/myservice.h b/server/myservice.h index 7f1cb88..bbf019c 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -133,6 +133,7 @@ class PortInfo pcap_t *devHandleRx; pcap_t *devHandleTx; QList sendQueueList; + int returnToQIdx; // FIXME(MED): combine with sendQList bool isSendQueueDirty; PcapExtra pcapExtra; PortMonitorRx monitorRx; diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 78d57e0..3246397 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -62,20 +62,32 @@ int pcap_sendqueue_queue (pcap_send_queue *queue, #endif u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int sync, + QList sendQueueList, int returnToQIdx, int sync, int *p_stop, quint64* p_pkts, quint64* p_bytes, void (*pf_usleep)(ulong)) { - uint ret = 0; + uint i, ret = 0; + ost_pcap_send_queue sq; - foreach(ost_pcap_send_queue sq, sendQueueList) + for(i = 0; i < sendQueueList.size(); i++) { +_restart: + sq = sendQueueList.at(i); ret += ost_pcap_sendqueue_transmit(p, sq.sendQueue, sync, p_stop, p_pkts, p_bytes, pf_usleep); + if (*p_stop) + return ret; + // TODO(HI): Timing between subsequent sendQueues } + if (returnToQIdx >= 0) + { + i = returnToQIdx; + goto _restart; + } + return ret; } diff --git a/server/pcapextra.h b/server/pcapextra.h index 3d2f8f4..5c597df 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -17,7 +17,7 @@ struct ost_pcap_send_queue // Common for all OS - *nix or Win32 u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int sync, + QList sendQueueList, int returnToQIdx, int sync, int *p_stop, quint64* p_pkts, quint64* p_bytes, void (*pf_usleep)(ulong)); From 238f332ac43005c0136b1995b174fdf74b1ca90d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 5 Apr 2009 07:19:37 +0000 Subject: [PATCH 019/294] - About Dialog added - Stream Dialog now remembers the "selected" tabs across close and reopen - Other trivial UI enhancements --- client/about.ui | 206 +++++++++++++++------------ client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/mainwindow.cpp | 41 ++++-- client/mainwindow.h | 15 +- client/mainwindow.ui | 117 +-------------- client/ostinato.pro | 1 + client/ostinato.qrc | 2 + client/portstatsfilter.ui | 6 +- client/portstatsmodel.cpp | 4 +- client/portstatswindow.ui | 6 + client/portswindow.cpp | 3 + client/portswindow.ui | 122 ++++++++-------- client/stream.cpp | 5 +- client/stream.h | 3 + client/streamconfigdialog.cpp | 12 +- client/streamconfigdialog.h | 6 + client/streamconfigdialog.ui | 2 +- server/pcapextra.cpp | 3 + 19 files changed, 271 insertions(+), 283 deletions(-) create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png diff --git a/client/about.ui b/client/about.ui index 2256d60..c7f914c 100644 --- a/client/about.ui +++ b/client/about.ui @@ -1,88 +1,118 @@ - - Dialog - - - - 0 - 0 - 400 - 300 - - - - Dialog - - - - - - QFrame::Box - - - QFrame::Raised - - - - - - <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - + + About + + + + 0 + 0 + 400 + 300 + + + + About + + + + + + QFrame::Box + + + QFrame::Raised + + + + + + Qt::Vertical + + + + 360 + 51 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2009 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/icons/portstats_clear.png b/client/icons/portstats_clear.png new file mode 100644 index 0000000000000000000000000000000000000000..48cfa9756ffa84b209ef6e18cfdb75c139d1c9f5 GIT binary patch literal 367 zcmV-#0g(QQP)%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn) #include "mainwindow.h" -#include "portswindow.h" -#include "portstatswindow.h" #include "portgrouplist.h" +#include "ui_about.h" + PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) @@ -11,20 +10,38 @@ MainWindow::MainWindow(QWidget *parent) { pgl = new PortGroupList; - PortsWindow *portsWindow = new PortsWindow(pgl, this); - PortStatsWindow *statsWindow = new PortStatsWindow(pgl, this); - QDockWidget *dock = new QDockWidget(tr("Ports"), this); - QDockWidget *dock2 = new QDockWidget(tr("Stats"), this); + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports"), this); + statsDock = new QDockWidget(tr("Stats"), this); setupUi(this); - dock2->setWidget(statsWindow); - addDockWidget(Qt::BottomDockWidgetArea, dock2); - dock->setWidget(portsWindow); - addDockWidget(Qt::TopDockWidgetArea, dock); + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); } -void MainWindow::on_actionPreferences_triggered() +MainWindow::~MainWindow() { + delete statsDock; + delete portsDock; + delete statsWindow; + delete portsWindow; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + + aboutDialog->exec(); + + delete aboutDialog; } diff --git a/client/mainwindow.h b/client/mainwindow.h index da412ec..a5d31a6 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -2,16 +2,29 @@ #define _MAIN_WINDOW_H #include +#include + #include "ui_mainwindow.h" +#include "portswindow.h" +#include "portstatswindow.h" + class MainWindow : public QMainWindow, private Ui::MainWindow { Q_OBJECT + +private: + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + public: MainWindow(QWidget *parent = 0); + ~MainWindow(); public slots: - void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); }; #endif diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 98ab4c2..1f93f5e 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -10,7 +10,7 @@ - MainWindow + Ostinato @@ -26,131 +26,26 @@ File - - - - - View - - - - - - Window - - - - - - - - + Help - + - - - - - Open Config - - - - - Save Config - - - - - Save Config As ... - - - - - Open Capture - - - - - Save Capture - - - - - Save Capture As ... - - - + E&xit - + - Copy Port Config - - - - - Paste Port Config - - - - - Preferences - - - - - Minimize - - - - - Maximize/Restore - - - - - Minimize All - - - - - Maximize/Restoe All - - - - - Arrange All - Cascade - - - - - Arrange All - Tile - - - - - Dock - - - - - Help - - - - - About + &About diff --git a/client/ostinato.pro b/client/ostinato.pro index ec44eb0..9bb3cd2 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -24,6 +24,7 @@ HEADERS += \ streammodel.h FORMS += \ + about.ui \ mainwindow.ui \ portstatsfilter.ui \ portstatswindow.ui \ diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 09fdd4a..7b735f7 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -12,6 +12,8 @@ icons/portgroup_connect.png icons/portgroup_delete.png icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png icons/portstats_filter.png icons/sound_mute.png icons/sound_none.png diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index a681c65..8423db3 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -5,12 +5,12 @@ 0 0 - 588 - 320 + 319 + 193 - Dialog + Select Ports diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index bcc0f54..9961e12 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -171,9 +171,9 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in getDomainIndexes(index(0, section), portGroupIdx, portIdx); #ifdef Q_OS_WIN32 - return QString("Port %1/%2 (*)").arg(portGroupIdx).arg(portIdx); + return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); #else - return QString("Port %1/%2").arg(portGroupIdx).arg(portIdx); + return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); #endif } else diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui index a7fc8d5..07fe1b2 100644 --- a/client/portstatswindow.ui +++ b/client/portstatswindow.ui @@ -65,6 +65,9 @@ Clear + + :/icons/portstats_clear.png + @@ -78,6 +81,9 @@ Clear All + + :/icons/portstats_clear_all.png + diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 1789e5e..8b75b65 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -61,6 +61,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) // Initially we don't have any ports/streams - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); when_streamView_currentChanged(QModelIndex(), QModelIndex()); + + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); } PortsWindow::~PortsWindow() diff --git a/client/portswindow.ui b/client/portswindow.ui index 99a32af..b58c356 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -6,7 +6,7 @@ 0 0 689 - 320 + 389 @@ -28,11 +28,11 @@ - 0 + 1 - - + + @@ -56,64 +56,59 @@ - - - - - - Qt::Horizontal - - - - 31 - 41 - - - - - - - - Capacity - - - - - - - - - - Aggr fps - - - - - - - - - - % age - - - - - - - - - - Aggr bps - - - - - - - + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Capacity + + + + + + + + + + Aggr fps + + + + + + + + + + % age + + + + + + + + + + Aggr bps + + + + + + + + - + 0 @@ -147,7 +142,10 @@ - Port Group Detail + Select a port to configure streams + + + Qt::AlignCenter diff --git a/client/stream.cpp b/client/stream.cpp index 2d82040..0afd8df 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -15,6 +15,7 @@ QString PayloadProtocol::fieldTextValue(int index) if (parentStream) { + qDebug("phs = %d", parentStream->protocolHeaderSize()); len = parentStream->frameLen() - parentStream->protocolHeaderSize(); pat = parentStream->pattern(); } @@ -48,6 +49,7 @@ QByteArray PayloadProtocol::fieldRawValue(int index) if (parentStream) { + qDebug("phs = %d", parentStream->protocolHeaderSize()); len = parentStream->frameLen() - parentStream->protocolHeaderSize(); pat = parentStream->pattern(); } @@ -1015,7 +1017,8 @@ bool Stream::update(OstProto::Stream *stream) mIp->update(stream->ip()); mArp->update(stream->arp()); - mTcp->update(stream->tcp()); + //mTcp->update(stream->tcp()); + mTcp->setProtoData(stream->mutable_tcp()); mUdp->update(stream->udp()); mIcmp->update(stream->icmp()); mIgmp->update(stream->igmp()); diff --git a/client/stream.h b/client/stream.h index 6e41702..434c008 100644 --- a/client/stream.h +++ b/client/stream.h @@ -33,6 +33,9 @@ public: void getConfig(::google::protobuf::Message *msg) { msg->CopyFrom(data()); } + bool setProtoData(::google::protobuf::Message *msg) + { data().MergeFrom(*msg); return true; } + virtual QString protocolName() { return QString("AbstractProtocol"); } virtual QString protocolShortName() diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index e7f1f0b..8b99561 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -4,6 +4,9 @@ #include "modeltest.h" +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtoTabIndex = 0; + // TODO(HI): Write HexLineEdit::setNum() and num() and use it in // Load/Store stream methods @@ -177,6 +180,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); + + // Finally, restore the saved last selected tab for the various tab widgets + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); + if (twProto->isTabEnabled(lastProtoTabIndex)) + twProto->setCurrentIndex(lastProtoTabIndex); } void StreamConfigDialog::setupUiExtra() @@ -230,8 +238,6 @@ void StreamConfigDialog::setupUiExtra() connect(rbModeContinuous, SIGNAL(toggled(bool)), this, SLOT(update_NumPacketsAndNumBursts())); - // Show "Packet Config" page by default - twTopLevel->setCurrentIndex(0); } StreamConfigDialog::~StreamConfigDialog() @@ -1115,6 +1121,8 @@ void StreamConfigDialog::on_pbOk_clicked() // Store dialog contents into stream StoreCurrentStream(); qDebug("stream stored"); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtoTabIndex = twProto->currentIndex(); } //Junk Line for introducing a compiler error diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index a8fe510..7dbedbe 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -34,6 +34,12 @@ private: PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time + static int lastTopLevelTabIndex; + static int lastProtoTabIndex; + void setupUiExtra(); void LoadCurrentStream(); void StoreCurrentStream(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 178e966..70fea95 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -13,7 +13,7 @@ - Dialog + Edit Stream QLineEdit:enabled[inputMask = "HH; "], diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 3246397..4ad8a51 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -85,6 +85,9 @@ _restart: if (returnToQIdx >= 0) { i = returnToQIdx; + + // FIXME: 1s fixed; Change this to ipg of last stream + (*pf_usleep)(1000000); goto _restart; } From 2d856012cb0fe18564e0d31c3045084b59127177 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 27 Apr 2009 16:51:44 +0000 Subject: [PATCH 020/294] New Protocol Framework - initial checkin; not yet complete --- Makefile | 7 +- client/dumpview.cpp | 99 +- client/hexlineedit.cpp | 8 +- client/mainwindow.cpp | 14 +- client/ostinato.pro | 10 +- client/packetmodel.cpp | 56 +- client/packetmodel.h | 93 +- client/port.cpp | 27 +- client/port.h | 4 +- client/portgroup.cpp | 3 +- client/portstatsfilter.ui | 7 +- client/stream.cpp | 1300 +++----------------------- client/stream.h | 905 +------------------ client/streamconfigdialog.cpp | 860 +++++++----------- client/streamconfigdialog.h | 21 +- client/streamconfigdialog.ui | 1601 +-------------------------------- client/streammodel.cpp | 12 +- common/Makefile | 14 - common/abstractprotocol.cpp | 225 +++++ common/abstractprotocol.h | 62 ++ common/dot3.cpp | 110 +++ common/dot3.h | 50 + common/dot3.proto | 12 + common/dot3.ui | 59 ++ common/eth2.cpp | 129 +++ common/eth2.h | 50 + common/eth2.proto | 12 + common/eth2.ui | 62 ++ common/ip4.cpp | 455 ++++++++++ common/ip4.h | 83 ++ common/ip4.proto | 47 + common/ip4.ui | 479 ++++++++++ common/llc.cpp | 139 +++ common/llc.h | 55 ++ common/llc.proto | 13 + common/llc.ui | 108 +++ common/mac.cpp | 207 +++++ common/mac.h | 63 ++ common/mac.proto | 29 + common/mac.ui | 190 ++++ common/ostproto.pro | 63 ++ common/payload.cpp | 176 ++++ common/payload.h | 55 ++ common/payload.proto | 22 + common/payload.ui | 69 ++ common/protocol.h | 169 ---- common/protocol.proto | 196 +--- common/snap.cpp | 112 +++ common/snap.h | 50 + common/snap.proto | 12 + common/snap.ui | 89 ++ common/tcp.cpp | 360 ++++++++ common/tcp.h | 69 ++ common/tcp.proto | 27 + common/tcp.ui | 228 +++++ common/udp.cpp | 200 ++++ common/udp.h | 56 ++ common/udp.proto | 18 + common/udp.ui | 101 +++ common/vlan.proto | 17 + common/vlan.ui | 168 ++++ rpc/pbrpc.pro | 7 +- server/drone.pro | 6 +- server/myservice.cpp | 186 +++- server/myservice.h | 19 +- 65 files changed, 5349 insertions(+), 4806 deletions(-) delete mode 100644 common/Makefile create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/ostproto.pro create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui delete mode 100644 common/protocol.h create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui diff --git a/Makefile b/Makefile index 8b555c4..c285580 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all: - $(MAKE) -C rpc install + $(MAKE) -C rpc $(MAKE) -C common $(MAKE) -C server $(MAKE) -C client @@ -11,4 +11,7 @@ clean: $(MAKE) -C client $@ qmake: - for %%d in (rpc common server client) cd %%d; qmake; cd..; + cd rpc && qmake && cd .. + cd common && qmake && cd .. + cd server && qmake && cd .. + cd client && qmake && cd .. diff --git a/client/dumpview.cpp b/client/dumpview.cpp index 79edab9..3393d5b 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -1,6 +1,7 @@ #include "dumpview.h" -//public: +//! \todo Enable Scrollbars + DumpView::DumpView(QWidget *parent) { int w, h; @@ -78,8 +79,8 @@ QModelIndex DumpView::indexAt( const QPoint &point ) const _exit: // Clear existing selection selrow = -1; -#endif +#endif return QModelIndex(); } @@ -148,55 +149,73 @@ void DumpView::selectionChanged( const QItemSelection &selected, void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, QModelIndex parent) { - // TODO(LOW): Assumption - only single selection - enforce/ensure this + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); for(int i = 0; i < model()->rowCount(parent); i++) { QModelIndex index = model()->index(i, 0, parent); - if (model()->hasChildren(index)) - { - // Non Leaf + Q_ASSERT(index.isValid()); - // A non-leaf has no dump data of its own but is rather a - // container for its children. So we calculate ofs/size based - // on this fact - if (selectionModel()->isSelected(index)) + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) { - selOfs = dump.size(); - populateDump(dump, selOfs, selSize, index); - selSize = dump.size() - selOfs; + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; } - else + + bits = 0; + j = index.row() - 1; + while (j >= 0) { - populateDump(dump, selOfs, selSize, index); + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); } else { - // Leaf - if (selectionModel()->isSelected(index)) + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) { - int size, j; - - selOfs = dump.size(); - size = model()->data(index, Qt::UserRole).toByteArray().size(); - - // Take care of multiple indexes (2 or more) mapping onto - // same dump byte(s) - j = i-1; - while ((size == 0) && (j >= 0)) - { - size = model()->data(index.sibling(j,0), Qt::UserRole). - toByteArray().size(); - selOfs -= size; - j++; - } - selSize = size; + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; } - dump.append(model()->data(index, Qt::UserRole).toByteArray()); - } - // FIXME: Use RawValueRole instead of UserRole + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } } } @@ -208,7 +227,7 @@ void DumpView::paintEvent(QPaintEvent* event) QRect dumpRect = mDumpPaneTopRect; QRect asciiRect = mAsciiPaneTopRect; QPalette pal = palette(); - QByteArray data; + static QByteArray data; //QByteArray ba; int selOfs = -1, selSize; int curSelOfs, curSelSize; @@ -222,7 +241,10 @@ void DumpView::paintEvent(QPaintEvent* event) painter.fillRect(rect(), QBrush(QColor(Qt::white))); if (model()) + { + data.clear(); populateDump(data, selOfs, selSize); + } // display the offset, dump and ascii panes 8 + 8 bytes on a line for (int i = 0; i < data.size(); i+=16) @@ -289,7 +311,8 @@ void DumpView::paintEvent(QPaintEvent* event) QRect r; QString selectedAsciiStr, selectedDumpStr; - qDebug("dumpview::paintEvent - Highlighted"); + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); // construct the dumpStr and asciiStr for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index 85d5ccf..dcb6c20 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -39,15 +39,17 @@ void HexLineEdit::focusOutEvent( QFocusEvent *e ) QLineEdit::focusOutEvent( e ); emit focusOut(); #else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + bool isOk; ulong num; - QString str; qDebug("before = %s\n", text().toAscii().data()); num = text().remove(QChar(' ')).toULong(&isOk, 16); - setText(uintToHexStr(num, str, 4)); + setText(uintToHexStr(num, 4)); qDebug("after = %s\n", text().toAscii().data()); - qDebug("after2 = %s\n", str.toAscii().data()); +#undef uintToHexStr #endif } diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 4e6fb64..0a0e0fb 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -1,6 +1,10 @@ #include "mainwindow.h" #include "portgrouplist.h" +#if 0 +#include "dbgthread.h" +#endif + #include "ui_about.h" PortGroupList *pgl; @@ -23,14 +27,16 @@ MainWindow::MainWindow(QWidget *parent) addDockWidget(Qt::TopDockWidgetArea, portsDock); connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif } MainWindow::~MainWindow() { - delete statsDock; - delete portsDock; - delete statsWindow; - delete portsWindow; } void MainWindow::on_actionHelpAbout_triggered() diff --git a/client/ostinato.pro b/client/ostinato.pro index 9bb3cd2..707969e 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,10 +1,13 @@ TEMPLATE = app CONFIG += qt debug QT += network -INCLUDEPATH += "../rpc/" +INCLUDEPATH += "../rpc/" "../common/" LIBS += -lprotobuf +win32:LIBS += -L"../common/debug" -lostproto +unix: LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc +POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ @@ -50,10 +53,5 @@ SOURCES += \ streamlistdelegate.cpp \ streammodel.cpp -# Protocol Buffer Sources - -SOURCES += \ - ..\common\protocol.pb.cc - # TODO(LOW): Test only include(modeltest.pri) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index a1d4b7f..f48f713 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,26 +1,36 @@ #include #include "packetmodel.h" -PacketModel::PacketModel(Stream *pStream, QObject *parent) +PacketModel::PacketModel(const QList &selectedProtocols, + QObject *parent) { - mpStream = pStream; + mSelectedProtocols = selectedProtocols; +} + +void PacketModel::setSelectedProtocols( + const QList &selectedProtocols) +{ + mSelectedProtocols = selectedProtocols; + reset(); } int PacketModel::rowCount(const QModelIndex &parent) const { IndexId parentId; + // qDebug("in %s", __FUNCTION__); + // Parent == Invalid i.e. Invisible Root. // ==> Children are Protocol (Top Level) Items if (!parent.isValid()) - return mpStream->numProtocols(); + return mSelectedProtocols.size(); // Parent - Valid Item parentId.w = parent.internalId(); switch(parentId.ws.type) { case ITYP_PROTOCOL: - return mpStream->protocol(parentId.ws.protocol)->numFields(); + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); case ITYP_FIELD: return 0; default: @@ -125,11 +135,13 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const switch(id.ws.type) { case ITYP_PROTOCOL: - return QByteArray(); + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); case ITYP_FIELD: - return mpStream->protocol(id.ws.protocol)->fieldRawValue( - index.row()); + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + index.row(), AbstractProtocol::FieldFrameValue); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); @@ -137,6 +149,25 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const return QByteArray(); } + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + index.row(), AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + if (role != Qt::DisplayRole) return QVariant(); @@ -144,13 +175,14 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const { case ITYP_PROTOCOL: return QString("%1 (%2)") - .arg(mpStream->protocol(id.ws.protocol)->protocolShortName()) - .arg(mpStream->protocol(id.ws.protocol)->protocolName()); + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); case ITYP_FIELD: - return mpStream->protocol(id.ws.protocol)->fieldName(index.row()) + - QString(" : ") + - mpStream->protocol(id.ws.protocol)->fieldTextValue(index.row()); + return mSelectedProtocols.at(id.ws.protocol)->fieldData(index.row(), + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(index.row(), + AbstractProtocol::FieldTextValue).toString(); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); diff --git a/client/packetmodel.h b/client/packetmodel.h index 1134a14..b268134 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -2,15 +2,16 @@ #define _PACKET_MODEL_H #include -#include "stream.h" - -#define NEW_IMPL // FIXME(HI) - Use this and remove old one +#include "abstractprotocol.h" class PacketModel: public QAbstractItemModel { public: - PacketModel(Stream *pStream, QObject *parent = 0); + PacketModel(const QList &selectedProtocols, + QObject *parent = 0); + void setSelectedProtocols( + const QList &selectedProtocols); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; @@ -33,89 +34,7 @@ private: } ws; } IndexId; - Stream *mpStream; - -#ifdef NEW_IMPL -// Nothing - required stuff is part of Stream Class -#else - QList mPacketProtocols; - - typedef struct - { - QString name; - QString abbr; - QString textValue; - } FieldInfo; - - typedef struct - { - uint handle; - QString name; - QString abbr; - QList fieldList; - } ProtocolInfo; - - //! Contains registration info (name, size etc) for all protocols - // and fields within the protocol - QList mProtocols; - void registerProto(uint handle, char *name, char *abbr); - void registerField(uint protoHandle, char *name, char *abbr); - - void registerFrameTypeProto(); - void registerVlanProto(); - void registerIpProto(); - void registerArpProto(); - void registerTcpProto(); - void registerUdpProto(); - void registerIcmpProto(); - void registerIgmpProto(); - void registerData(); - void registerInvalidProto(); - - void populatePacketProtocols(); - - int protoCount() const; - int fieldCount(int protocol) const; - QString protoName(int protocol) const; - QString fieldName(int protocol, int field) const; - QVariant fieldTextValue(int protocol, int field) const; - - QVariant ethField(int field, int role) const; - QVariant llcField(int field, int role) const; - QVariant snapField(int field, int role) const; - QVariant svlanField(int field, int role) const; - QVariant ipField(int field, int role) const; - QVariant tcpField(int field, int role) const; - QVariant udpField(int field, int role) const; - -// FIXME(HIGH): Is this how I want this? -#define PTYP_L2_NONE 1 -#define PTYP_L2_ETH_2 2 -#define PTYP_L2_802_3_RAW 3 - -#define PTYP_L2_802_3_LLC 4 -#define PTYP_L2_SNAP 5 - -#define PTYP_SVLAN 10 -#define PTYP_CVLAN 11 - -#define PTYP_L3_IP 30 -#define PTYP_L3_ARP 31 - -#define PTYP_L4_TCP 40 -#define PTYP_L4_UDP 41 -#define PTYP_L4_ICMP 42 -#define PTYP_L4_IGMP 43 - -#define PTYP_INVALID 0 -#define PTYP_DATA 0xFF - -#endif // NEW_IMPL - -#define FROL_NAME 1 -#define FROL_TEXT_VALUE 2 - -#define BASE_HEX 16 + QList mSelectedProtocols; }; #endif diff --git a/client/port.cpp b/client/port.cpp index 3ee571e..64c7865 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -32,7 +32,7 @@ void Port::updatePortConfig(OstProto::Port *port) void Port::updateStreamOrdinalsFromIndex() { for (int i=0; i < mStreams.size(); i++) - mStreams[i].setOrdinal(i); + mStreams[i]->setOrdinal(i); } void Port::reorderStreamsByOrdinals() @@ -42,12 +42,12 @@ void Port::reorderStreamsByOrdinals() bool Port::newStreamAt(int index) { - Stream s; + Stream *s = new Stream; if (index > mStreams.size()) return false; - s.setId(newStreamId()); + s->setId(newStreamId()); mStreams.insert(index, s); updateStreamOrdinalsFromIndex(); @@ -59,7 +59,7 @@ bool Port::deleteStreamAt(int index) if (index >= mStreams.size()) return false; - mStreams.removeAt(index); + delete mStreams.takeAt(index); updateStreamOrdinalsFromIndex(); return true; @@ -67,9 +67,9 @@ bool Port::deleteStreamAt(int index) bool Port::insertStream(uint streamId) { - Stream s; + Stream *s = new Stream; - s.setId(streamId); + s->setId(streamId); // FIXME(MED): If a stream with id already exists, what do we do? mStreams.append(s); @@ -88,7 +88,7 @@ bool Port::updateStream(uint streamId, OstProto::Stream *stream) for (i = 0; i < mStreams.size(); i++) { - if (streamId == mStreams[i].id()) + if (streamId == mStreams[i]->id()) goto _found; } @@ -98,7 +98,7 @@ bool Port::updateStream(uint streamId, OstProto::Stream *stream) _found: streamIndex = i; - mStreams[streamIndex].update(stream); + mStreams[streamIndex]->update(stream); reorderStreamsByOrdinals(); return true; @@ -114,7 +114,7 @@ void Port::getDeletedStreamsSinceLastSync( for (j = 0; j < mStreams.size(); j++) { - if (mLastSyncStreamList[i] == mStreams[j].id()) + if (mLastSyncStreamList[i] == mStreams[j]->id()) break; } @@ -140,7 +140,7 @@ void Port::getNewStreamsSinceLastSync( streamIdList.clear_stream_id(); for (int i = 0; i < mStreams.size(); i++) { - if (mLastSyncStreamList.contains(mStreams[i].id())) + if (mLastSyncStreamList.contains(mStreams[i]->id())) { // existing stream! continue; @@ -151,7 +151,7 @@ void Port::getNewStreamsSinceLastSync( OstProto::StreamId *s; s = streamIdList.add_stream_id(); - s->set_id(mStreams[i].id()); + s->set_id(mStreams[i]->id()); } } } @@ -167,8 +167,9 @@ void Port::getModifiedStreamsSinceLastSync( OstProto::Stream *s; s = streamConfigList.add_stream(); - mStreams[i].getConfig(mPortId, s); + mStreams[i]->getConfig(mPortId, *s); } + qDebug("Done %s", __FUNCTION__); } void Port::when_syncComplete() @@ -177,7 +178,7 @@ void Port::when_syncComplete() mLastSyncStreamList.clear(); for (int i=0; iid()); } void Port::updateStats(OstProto::PortStats *portStats) diff --git a/client/port.h b/client/port.h index c10eeb3..126b28d 100644 --- a/client/port.h +++ b/client/port.h @@ -23,7 +23,7 @@ private: QString mUserAlias; // user defined QList mLastSyncStreamList; - QList mStreams; // sorted by stream's ordinal value + QList mStreams; // sorted by stream's ordinal value uint newStreamId(); void updateStreamOrdinalsFromIndex(); @@ -65,7 +65,7 @@ public: //void setExclusive(bool flag); int numStreams() { return mStreams.size(); } - Stream& streamByIndex(int index) + Stream* streamByIndex(int index) { Q_ASSERT(index < mStreams.size()); return mStreams[index]; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 4630c72..f9f84d2 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,5 +1,4 @@ #include "portgroup.h" -#include "../common/protocol.h" #include @@ -400,7 +399,7 @@ _request: OstProto::StreamId *s; s = streamIdList.add_stream_id(); - s->set_id(mPorts[portIndex].streamByIndex(j).id()); + s->set_id(mPorts[portIndex].streamByIndex(j)->id()); } streamConfigList->Clear(); diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index 8423db3..af9af02 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -12,6 +12,9 @@ Select Ports + + :/icons/portstats_filter.png + @@ -121,7 +124,9 @@ lvSelected buttonBox - + + + buttonBox diff --git a/client/stream.cpp b/client/stream.cpp index 0afd8df..3af3509 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -3,951 +3,21 @@ #include "stream.h" -#include - -#define BASE_HEX 16 - -QString PayloadProtocol::fieldTextValue(int index) -{ - int len; - quint32 pat; - QString textValue; - - if (parentStream) - { - qDebug("phs = %d", parentStream->protocolHeaderSize()); - len = parentStream->frameLen() - parentStream->protocolHeaderSize(); - pat = parentStream->pattern(); - } - else - { - len = 1500; // FIXME(HI): testing only - pat = 0x0a0b0c0d; - } - - // Make a larger string and then resize to the correct size to - // take care of the case where len is not a multiple of pattern size - if (len > 0) - { - // TODO(LOW): allow non-4byte patterns!!! - int w = 4; // data pattern size - - for (int i = 0; i < (len/w + 1); i++) - textValue.append(QString("%1").arg( - pat, w*2, BASE_HEX, QChar('0'))); - textValue.resize(len); - } - - return textValue; -} - -QByteArray PayloadProtocol::fieldRawValue(int index) -{ - int len; - quint32 pat; - QByteArray rawValue; - - if (parentStream) - { - qDebug("phs = %d", parentStream->protocolHeaderSize()); - len = parentStream->frameLen() - parentStream->protocolHeaderSize(); - pat = parentStream->pattern(); - } - else - { - len = 1500; // FIXME(HI): testing only - pat = 0x0a0b0c0d; - } - - // Make a larger byteArray and then resize to the correct size to - // take care of the case where len is not a multiple of pattern size - if (len > 0) - { - // TODO(LOW): allow non-4byte patterns!!! - int w = 4; // data pattern size - - rawValue.resize(len + 4); - for (int i = 0; i < (len/w + 1); i++) - qToBigEndian(pat, (uchar*) (rawValue.data() + i*sizeof(pat))); - rawValue.resize(len); - } - - return rawValue; -} - -QString MacProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Destination Mac Address"); - break; - case 1: - name = QString("Source Mac Address"); - break; - default: - name = QString(); - break; - } - - return name; -} - -QString MacProtocol::fieldTextValue(int index) -{ - QString textValue; - - // FIXME(MED): Mac Addr formatting - switch(index) - { - case 0: - textValue = QString("%1"). - arg(dstMac(), 12, BASE_HEX, QChar('0')); - break; - case 1: - textValue = QString("%1"). - arg(srcMac(), 12, BASE_HEX, QChar('0')); - break; - default: - textValue = QString(); - break; - } - - return textValue; -} - -QByteArray MacProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(8); - qToBigEndian(dstMac(), (uchar *) rawValue.data()); - rawValue.remove(0, 2); - qDebug("dstMac(%d): %s", rawValue.size(), rawValue.toHex().constData()); - break; - case 1: - rawValue.resize(8); - qToBigEndian(srcMac(), (uchar *) rawValue.data()); - rawValue.remove(0, 2); - qDebug("srcMac(%d): %s", rawValue.size(), rawValue.toHex().constData()); - break; - default: - break; - } - - return rawValue; -} - -QString LlcProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("DSAP"); - break; - case 1: - name = QString("SSAP"); - break; - case 2: - name = QString("Control"); - break; - default: - name = QString(); - break; - } - - return name; -} - -QString LlcProtocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("0x%1"). - arg(dsap(), 2, BASE_HEX, QChar('0')); - break; - case 1: - textValue = QString("0x%1"). - arg(ssap(), 2, BASE_HEX, QChar('0')); - break; - case 2: - textValue = QString("0x%1"). - arg(ctl(), 2, BASE_HEX, QChar('0')); - break; - default: - textValue = QString(); - break; - } - - return textValue; -} - -QByteArray LlcProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(1); - rawValue[0] = dsap(); - break; - case 1: - rawValue.resize(1); - rawValue[0] = ssap(); - break; - case 2: - rawValue.resize(1); - rawValue[0] = ctl(); - break; - default: - break; - } - - return rawValue; -} - -QString SnapProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("OUI"); - break; - default: - name = QString(); - break; - } - - return name; -} - -QString SnapProtocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("0x%1"). - arg(oui(), 6, BASE_HEX, QChar('0')); - break; - default: - textValue = QString(); - break; - } - - return textValue; -} - -QByteArray SnapProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(4); - qToBigEndian(oui(), (uchar *) rawValue.data()); - rawValue.remove(0, 1); - break; - default: - break; - } - - return rawValue; -} - -QString Eth2Protocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Type"); - break; - default: - name = QString(); - break; - } - return name; -} - -QString Eth2Protocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("0x%1"). - arg(type(), 4, BASE_HEX, QChar('0')); - break; - default: - textValue = QString(); - break; - } - return textValue; -} - -QByteArray Eth2Protocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(2); - qToBigEndian(type(), (uchar*) rawValue.data()); - break; - default: - break; - } - - return rawValue; -} - -QString Dot3Protocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Length"); - break; - default: - name = QString(); - break; - } - return name; -} - -QString Dot3Protocol::fieldTextValue(int index) -{ - if (parentStream) - return QString("%1").arg(parentStream->frameLen()); - else - return QString("00"); -} - -QByteArray Dot3Protocol::fieldRawValue(int index) -{ - QByteArray ba; - - if (parentStream) - qToBigEndian((quint16) parentStream->frameLen(), (uchar*) ba.data()); - else - { - ba.resize(2); - ba[0] = ba[1] = 0; - } - - return ba; -} - -int VlanProtocol::numFields() -{ - if (isSingleTagged()) - return 4; - else if (isDoubleTagged()) - return 8; - else - { - Q_ASSERT(isUntagged()); - return 0; - } -} - -QString VlanProtocol::fieldName(int index) -{ - QString name; - - if (isDoubleTagged()) - { - switch(index) - { - case 0: - name = QString("TPID"); - break; - case 1: - name = QString("PCP"); - break; - case 2: - name = QString("DE"); - break; - case 3: - name = QString("VlanId"); - break; - default: - index -= 4; - goto _single_tag; - } - - goto _exit; - } - -_single_tag: - switch(index) - { - case 0: - name = QString("TPID"); - break; - case 1: - name = QString("Priority"); - break; - case 2: - name = QString("CFI"); - break; - case 3: - name = QString("VlanId"); - break; - default: - name = QString(); - break; - } - -_exit: - return name; -} - -QString VlanProtocol::fieldTextValue(int index) -{ - QString textValue; - - if (isDoubleTagged()) - { - switch(index) - { - case 0: - textValue = QString("0x%1"). - arg(stpid(), 4, BASE_HEX, QChar('0')); - break; - case 1: - textValue = QString("%1"). - arg(svlanPrio()); - break; - case 2: - textValue = QString("%1"). - arg(svlanCfi()); - break; - case 3: - textValue = QString("%1"). - arg(svlanId()); - break; - default: - index -= 4; - goto _single_tag; - } - - goto _exit; - } - -_single_tag: - switch(index) - { - case 0: - textValue = QString("0x%1"). - arg(ctpid(), 4, BASE_HEX, QChar('0')); - break; - case 1: - textValue = QString("%1"). - arg(cvlanPrio()); - break; - case 2: - textValue = QString("%1"). - arg(cvlanCfi()); - break; - case 3: - textValue = QString("%1"). - arg(cvlanId()); - break; - default: - textValue = QString(); - break; - } - -_exit: - return textValue; -} - -QByteArray VlanProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - if (isDoubleTagged()) - { - switch(index) - { - case 0: - rawValue.resize(2); - qToBigEndian(stpid(), (uchar*) rawValue.data()); - break; - case 1: - rawValue.resize(2); - qToBigEndian((svlanPrio() << 13) | (svlanCfi() < 12) | svlanId(), - (uchar*) rawValue.data()); - break; - case 2: - // Combined with prio above - break; - case 3: - // Combined with prio above - break; - default: - index -= 4; - goto _single_tag; - } - - goto _exit; - } - -_single_tag: - switch(index) - { - case 0: - rawValue.resize(2); - qToBigEndian(ctpid(), (uchar*) rawValue.data()); - break; - case 1: - rawValue.resize(2); - qToBigEndian((cvlanPrio() << 13) | (cvlanCfi() < 12) | cvlanId(), - (uchar*) rawValue.data()); - break; - case 2: - // Combined with prio above - break; - case 3: - // Combined with prio above - break; - default: - break; - } - -_exit: - return rawValue; -} - -QString IpProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Version"); - break; - case 1: - name = QString("Header Length"); - break; - case 2: - name = QString("TOS/DSCP"); - break; - case 3: - name = QString("Total Length"); - break; - case 4: - name = QString("ID"); - break; - case 5: - name = QString("Flags"); - break; - case 6: - name = QString("Fragment Offset"); - break; - case 7: - name = QString("TTL"); - break; - case 8: - name = QString("Protocol Type"); - break; - case 9: - name = QString("Checksum"); - break; - case 10: - name = QString("Source IP"); - break; - case 11: - name = QString("Destination IP"); - break; - default: - name = QString(); - } - - return name; -} - -QString IpProtocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("%1"). - arg(ver()); - break; - case 1: - textValue = QString("%1"). - arg(hdrLen()); - break; - case 2: - textValue = QString("0x%1"). - arg(tos(), 2, BASE_HEX, QChar('0')); - break; - case 3: - textValue = QString("%1"). - arg(totLen()); - break; - case 4: - textValue = QString("0x%1"). - arg(id(), 2, BASE_HEX, QChar('0')); - break; - case 5: - textValue = QString("0x%1"). - arg(flags(), 2, BASE_HEX, QChar('0')); // FIXME(MED): bitmap? - break; - case 6: - textValue = QString("%1"). - arg(fragOfs()); - break; - case 7: - textValue = QString("%1"). - arg(ttl()); - break; - case 8: - textValue = QString("0x%1"). - arg(proto(), 2, BASE_HEX, QChar('0')); - break; - case 9: - textValue = QString("0x%1"). - arg(cksum(), 4, BASE_HEX, QChar('0')); - break; - case 10: - textValue = QHostAddress(srcIp()).toString(); - break; - case 11: - textValue = QHostAddress(dstIp()).toString(); - break; - default: - textValue = QString(); - } - - return textValue; -} - -QByteArray IpProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(1); - //qToBigEndian((ver() << 4) | hdrLen(), (uchar*) rawValue.data()); - rawValue[0]=(ver() << 4) | hdrLen(); - break; - case 1: - // Combined with previous 4 bits of ver!! - break; - case 2: - rawValue.resize(1); - qToBigEndian(tos(), (uchar*) rawValue.data()); - break; - case 3: - rawValue.resize(2); - qToBigEndian(totLen(), (uchar*) rawValue.data()); - break; - case 4: - rawValue.resize(2); - qToBigEndian(id(), (uchar*) rawValue.data()); - break; - case 5: - rawValue.resize(2); - qToBigEndian((quint16)((flags() << 13) | fragOfs()), - (uchar*) rawValue.data()); - break; - case 6: - // Combined with previous 3 bits of flags!! - break; - case 7: - rawValue.resize(1); - qToBigEndian(ttl(), (uchar*) rawValue.data()); - break; - case 8: - rawValue.resize(1); - qToBigEndian(proto(), (uchar*) rawValue.data()); - break; - case 9: - rawValue.resize(2); - qToBigEndian(cksum(), (uchar*) rawValue.data()); - break; - case 10: - rawValue.resize(4); - qToBigEndian(srcIp(), (uchar*) rawValue.data()); - break; - case 11: - rawValue.resize(4); - qToBigEndian(dstIp(), (uchar*) rawValue.data()); - break; - default: - break; - } - - return rawValue; -} - -QString TcpProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Source Port"); - break; - case 1: - name = QString("Destination Port"); - break; - case 2: - name = QString("Seq Number"); - break; - case 3: - name = QString("Ack Number"); - break; - case 4: - name = QString("Header Length"); - break; - case 5: - name = QString("Reserved"); - break; - case 6: - name = QString("Flags"); - break; - case 7: - name = QString("Window"); - break; - case 8: - name = QString("Checksum"); - break; - case 9: - name = QString("Urgent Pointer"); - break; - default: - name = QString(); - } - - return name; -} - -QString TcpProtocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("%1"). - arg(srcPort()); - break; - case 1: - textValue = QString("%1"). - arg(dstPort()); - break; - case 2: - textValue = QString("%1"). - arg(seqNum()); - break; - case 3: - textValue = QString("%1"). - arg(ackNum()); - break; - case 4: - textValue = QString("%1"). - arg(hdrLen()); - break; - case 5: - textValue = QString("%1"). - arg(rsvd()); - break; - case 6: - textValue = QString("0x%1"). - arg(flags(), 2, BASE_HEX, QChar('0')); - break; - case 7: - textValue = QString("%1"). - arg(window(), 2, BASE_HEX, QChar('0')); - break; - case 8: - textValue = QString("0x%1"). - arg(cksum(), 4, BASE_HEX, QChar('0')); - break; - case 9: - textValue = QString("%1"). - arg(urgPtr()); - break; - default: - textValue = QString(); - } - - return textValue; -} - -QByteArray TcpProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(2); - qToBigEndian(srcPort(), (uchar*) rawValue.data()); - break; - case 1: - rawValue.resize(2); - qToBigEndian(dstPort(), (uchar*) rawValue.data()); - break; - case 2: - rawValue.resize(4); - qToBigEndian(seqNum(), (uchar*) rawValue.data()); - break; - case 3: - rawValue.resize(4); - qToBigEndian(ackNum(), (uchar*) rawValue.data()); - break; - case 4: - rawValue.resize(1); - rawValue[0] = (hdrLen() << 4) | rsvd(); - break; - case 5: - // Combined with hdrLen above - break; - case 6: - rawValue.resize(1); - rawValue[0] = flags(); - break; - case 7: - rawValue.resize(2); - qToBigEndian(window(), (uchar*) rawValue.data()); - break; - case 8: - rawValue.resize(2); - qToBigEndian(cksum(), (uchar*) rawValue.data()); - break; - case 9: - rawValue.resize(2); - qToBigEndian(urgPtr(), (uchar*) rawValue.data()); - break; - default: - break; - } - - return rawValue; -} - -QString UdpProtocol::fieldName(int index) -{ - QString name; - - switch(index) - { - case 0: - name = QString("Source Port"); - break; - case 1: - name = QString("Destination Port"); - break; - case 2: - name = QString("Total Length"); - break; - case 3: - name = QString("Checksum"); - break; - default: - name = QString(); - } - - return name; -} - -QString UdpProtocol::fieldTextValue(int index) -{ - QString textValue; - - switch(index) - { - case 0: - textValue = QString("%1"). - arg(srcPort()); - break; - case 1: - textValue = QString("%1"). - arg(dstPort()); - break; - case 2: - textValue = QString("%1"). - arg(totLen()); - break; - case 3: - textValue = QString("0x%1"). - arg(cksum(), 4, BASE_HEX, QChar('0')); - break; - default: - textValue = QString(); - } - - return textValue; -} - -QByteArray UdpProtocol::fieldRawValue(int index) -{ - QByteArray rawValue; - - switch(index) - { - case 0: - rawValue.resize(2); - qToBigEndian(srcPort(), (uchar*) rawValue.data()); - break; - case 1: - rawValue.resize(2); - qToBigEndian(dstPort(), (uchar*) rawValue.data()); - break; - case 2: - rawValue.resize(2); - qToBigEndian(totLen(), (uchar*) rawValue.data()); - break; - case 3: - rawValue.resize(2); - qToBigEndian(cksum(), (uchar*) rawValue.data()); - break; - default: - break; - } - - return rawValue; -} - +#include "../common/mac.h" +#include "../common/payload.h" + +#include "../common/eth2.h" // FIXME: proto DB +#include "../common/dot3.h" // FIXME: proto DB +#include "../common/llc.h" // FIXME: proto DB +#include "../common/snap.h" // FIXME: proto DB +#include "../common/ip4.h" // FIXME: proto DB +#include "../common/tcp.h" // FIXME: proto DB +#include "../common/udp.h" // FIXME: proto DB //----------------------------------------------------- // Stream Class Methods //----------------------------------------------------- - - Stream::Stream() { mId = 0xFFFFFFFF; @@ -958,274 +28,148 @@ Stream::Stream() // mCore->set_port_id(0xFFFFFFFF); // mCore->set_stream_id(mId); - mUnknown = new UnknownProtocol; - mPayload = new PayloadProtocol(); //FIXME(MED): need to pass parent stream + mProtocolList.append(new MacProtocol); + mProtocolList.append(new PayloadProtocol(this)); - mMac = new MacProtocol; - - mLlc = new LlcProtocol; - mSnap = new SnapProtocol; - mEth2 = new Eth2Protocol; - mDot3 = new Dot3Protocol(); // FIXME(MED): need to pass parent stream - mVlan = new VlanProtocol; - - mIp = new IpProtocol; - mArp = new ArpProtocol; - - mTcp = new TcpProtocol; - mUdp = new UdpProtocol; - mIcmp = new IcmpProtocol; - mIgmp = new IgmpProtocol; + // FIXME: proto DB + mProtocolList.append(new Eth2Protocol); + mProtocolList.append(new Dot3Protocol); + mProtocolList.append(new LlcProtocol); + mProtocolList.append(new SnapProtocol); + mProtocolList.append(new Ip4Protocol); + mProtocolList.append(new TcpProtocol); + mProtocolList.append(new UdpProtocol); mCore->set_is_enabled(true); + mCore->add_frame_proto(51); // MAC (FIXME: hardcoding) + mCore->add_frame_proto(52); // Payload (FIXME: hardcoding) } -void Stream::getConfig(uint portId, OstProto::Stream *s) +Stream::~Stream() { - s->mutable_stream_id()->set_id(mId); + for (int i = 0; i < mProtocolList.size(); i++) + delete mProtocolList.at(i); - s->mutable_core()->CopyFrom(*mCore); - s->mutable_control()->CopyFrom(*mControl); + delete mControl; + delete mCore; +} - mMac->getConfig(s->mutable_mac()); - mMac->getConfig(s->mutable_mac()); - mLlc->getConfig(s->mutable_llc()); - mSnap->getConfig(s->mutable_snap()); - mEth2->getConfig(s->mutable_eth2()); - mVlan->getConfig(s->mutable_vlan()); +void Stream::protoDataCopyFrom(Stream& stream) +{ + OstProto::Stream data; - mIp->getConfig(s->mutable_ip()); - mArp->getConfig(s->mutable_arp()); + stream.getConfig(0, data); + update(&data); +} - mTcp->getConfig(s->mutable_tcp()); - mUdp->getConfig(s->mutable_udp()); - mIcmp->getConfig(s->mutable_icmp()); - mIgmp->getConfig(s->mutable_igmp()); +void Stream::loadProtocolWidgets() +{ + for (int i=0; i < mProtocolList.size(); i++) + mProtocolList[i]->loadConfigWidget(); +} + +void Stream::storeProtocolWidgets() +{ + for (int i=0; i < mProtocolList.size(); i++) + mProtocolList[i]->storeConfigWidget(); +} + +/*! Copy current client side config into the OstProto::Stream */ +// FIXME - remove portId unused param! +void Stream::getConfig(uint portId, OstProto::Stream &s) +{ + s.mutable_stream_id()->set_id(mId); + + s.mutable_core()->CopyFrom(*mCore); + s.mutable_control()->CopyFrom(*mControl); + + // FIXME - this doesn't take care of multiple headers of same proto + // e.g. IPinIP or double VLAN Tagged + // FIXME: change s from pointer to reference? + for (int i = 0; i < mProtocolList.size(); i++) + { + qDebug("%s: protocol %d", __FUNCTION__, i); + mProtocolList[i]->protoDataCopyInto(s); + } + + qDebug("%s: Done", __FUNCTION__); } bool Stream::update(OstProto::Stream *stream) - { - mCore->MergeFrom(stream->core()); - mControl->MergeFrom(stream->control()); - mMac->update(stream->mac()); - - mLlc->update(stream->llc()); - mSnap->update(stream->snap()); - mEth2->update(stream->eth2()); - mVlan->update(stream->vlan()); - - mIp->update(stream->ip()); - mArp->update(stream->arp()); - - //mTcp->update(stream->tcp()); - mTcp->setProtoData(stream->mutable_tcp()); - mUdp->update(stream->udp()); - mIcmp->update(stream->icmp()); - mIgmp->update(stream->igmp()); - - // FIXME(MED): Re-eval why not store complete OstProto::Stream - // instead of components - return true; - } -// FIXME(HIGH): Replace this by some Protocol Registration mechanism at Init -#define PTYP_L2_NONE 1 -#define PTYP_L2_ETH_2 2 -#define PTYP_L2_802_3_RAW 3 - -#define PTYP_L2_802_3_LLC 4 -#define PTYP_L2_SNAP 5 - -#define PTYP_VLAN 10 - -#define PTYP_L3_IP 30 -#define PTYP_L3_ARP 31 - -#define PTYP_L4_TCP 40 -#define PTYP_L4_UDP 41 -#define PTYP_L4_ICMP 42 -#define PTYP_L4_IGMP 43 - -#define PTYP_INVALID 0 -#define PTYP_DATA 0xFF - -void Stream::updateSelectedProtocols() { - int proto; + mCore->clear_frame_proto(); + mCore->MergeFrom(stream->core()); + mControl->MergeFrom(stream->control()); - // Clear the selected protocols list - selectedProtocols.clear(); - - // Check and populate L2 Protocol - switch(frameType()) + // FIXME - this doesn't take care of multiple headers of same proto + // e.g. IPinIP or double VLAN Tagged + // FIXME: change s from pointer to reference? + for (int i = 0; i < mProtocolList.size(); i++) { - case Stream::e_ft_none: - proto = PTYP_L2_NONE; - break; - - case Stream::e_ft_eth_2: - selectedProtocols.append(PTYP_L2_NONE); - proto = PTYP_L2_ETH_2; - break; - - case Stream::e_ft_802_3_raw: - selectedProtocols.append(PTYP_L2_NONE); - proto = PTYP_L2_802_3_RAW; - break; - - case Stream::e_ft_802_3_llc: - selectedProtocols.append(PTYP_L2_NONE); - proto = PTYP_L2_802_3_LLC; - break; - - case Stream::e_ft_snap: - selectedProtocols.append(PTYP_L2_NONE); - selectedProtocols.append(PTYP_L2_802_3_LLC); - proto = PTYP_L2_SNAP; - break; - - default: - qDebug("%s: Unsupported frametype %d", __FUNCTION__, - frameType()); - proto = PTYP_INVALID; + mProtocolList[i]->protoDataCopyFrom(*stream); } - selectedProtocols.append(proto); - // Check and populate VLANs, if present - if (!vlan()->isUntagged()) - selectedProtocols.append(PTYP_VLAN); + // FIXME(MED): Re-eval why not store complete OstProto::Stream + // instead of components + return true; +} - // Check and populate L3 protocols - switch (l3Proto()) - { - case Stream::e_l3_none : - goto _data; - break; +QList Stream::frameProtocol() +{ + QList protocolList; - case Stream::e_l3_ip : - proto = PTYP_L3_IP; - break; + for (int i = 0; i < mCore->frame_proto_size(); i++) + protocolList.append(mCore->frame_proto(i)); - case Stream::e_l3_arp: - proto = PTYP_L3_ARP; - break; + return protocolList; +} - default: - qDebug("%s: Unsupported L3 Proto %d", __FUNCTION__, - l3Proto()); - proto = PTYP_INVALID; - } - selectedProtocols.append(proto); - - // Check and populate L4 protocol - switch(l4Proto()) - { - case Stream::e_l4_none: - goto _data; - break; - case Stream::e_l4_tcp: - proto = PTYP_L4_TCP; - break; - case Stream::e_l4_udp: - proto = PTYP_L4_UDP; - break; - case Stream::e_l4_icmp: - proto = PTYP_L4_ICMP; - break; - case Stream::e_l4_igmp: - proto = PTYP_L4_IGMP; - break; - default: - qDebug("%s: Unsupported L4 Proto %d", __FUNCTION__, - l4Proto()); - proto = PTYP_INVALID; - }; - selectedProtocols.append(proto); - -_data: - - mProtocolHeaderSize = 0; -#ifndef SRIVATSP -#if 0 - for (int i = 0; i < selectedProtocols.size(); i++) - mProtocolHeaderSize += protocol(i)->protocolRawValue().size(); -#endif -#endif - selectedProtocols.append(PTYP_DATA); +void Stream::setFrameProtocol(QList protocolList) +{ + mCore->clear_frame_proto(); + for (int i = 0; i < protocolList.size(); i++) + mCore->add_frame_proto(protocolList.at(i)); } int Stream::protocolHeaderSize() { - updateSelectedProtocols(); // FIXME(HI): shd not happen everytime - return mProtocolHeaderSize; + int size = 0; + + for (int i = 0; i < mCore->frame_proto_size(); i++) + size += protocolById(mCore->frame_proto(i))-> + protocolFrameValue().size(); + + return size; } -int Stream::numProtocols() +AbstractProtocol* Stream::protocolById(int id) { - updateSelectedProtocols(); // FIXME(HI): shd not happen everytime - return selectedProtocols.size(); -} - -#if 0 -int Stream::protocolId(int index) -{ - updateSelectedProtocols(); // FIXME(HI): shd not happen everytime - if (index < selectedProtocols.size()) - return selectedProtocols.at(index); - else - return -1; -} -int Stream::protocolIndex(int id) -{ - -} + // FIXME BAD BAD VERY BAD! + switch(id) { + case 51: + return mProtocolList.at(0); + case 52: + return mProtocolList.at(1); + case 121: + return mProtocolList.at(2); + case 122: + return mProtocolList.at(3); + case 123: + return mProtocolList.at(4); + case 124: + return mProtocolList.at(5); + // case 125 (unused) +#if 0 // todo VLAN + case 126: + return mProtocolList.at(x); #endif - -AbstractProtocol* Stream::protocol(int index) -{ - int id; - - updateSelectedProtocols(); // FIXME(HI): shd not happen everytime - - id = selectedProtocols.at(index); - - switch(id) - { - case PTYP_L2_NONE: - return mac(); - case PTYP_L2_ETH_2: - return eth2(); - case PTYP_L2_802_3_RAW: - return dot3(); - - case PTYP_L2_802_3_LLC: - return llc(); - - case PTYP_L2_SNAP: - return snap(); - - case PTYP_VLAN: - return vlan(); - - case PTYP_L3_IP: - return ip(); - case PTYP_L3_ARP: - return arp(); - - case PTYP_L4_TCP: - return tcp(); - case PTYP_L4_UDP: - return udp(); - case PTYP_L4_ICMP: - return icmp(); - case PTYP_L4_IGMP: - return igmp(); - - case PTYP_INVALID: - return mUnknown; - case PTYP_DATA: - return mPayload; + case 130: + return mProtocolList.at(6); + case 140: + return mProtocolList.at(7); + case 141: + return mProtocolList.at(8); default: - return mUnknown; + return NULL; } } - diff --git a/client/stream.h b/client/stream.h index 434c008..c4884d4 100644 --- a/client/stream.h +++ b/client/stream.h @@ -6,10 +6,7 @@ #include #include #include "../common/protocol.pb.h" - -class StreamConfigDialog; -class StreamModel; -class PacketModel; +#include "../common/abstractprotocol.h" // Convenience Defines FIXME #define IP_PROTO_ICMP 0x01 @@ -17,864 +14,19 @@ class PacketModel; #define IP_PROTO_TCP 0x06 #define IP_PROTO_UDP 0x11 - -class Stream; - -class AbstractProtocol -{ - // TODO(LOW) -public: - /*! - Subclasses should return reference to their protocol specific - ::google::protobuf::Message - */ - virtual ::google::protobuf::Message& data() = 0; - /*! Subclasses can directly use this method. No need for overload */ - void getConfig(::google::protobuf::Message *msg) - { msg->CopyFrom(data()); } - - bool setProtoData(::google::protobuf::Message *msg) - { data().MergeFrom(*msg); return true; } - - virtual QString protocolName() - { return QString("AbstractProtocol"); } - virtual QString protocolShortName() - { return QString("AbsProto"); } - virtual int numFields() - { return 1; } - QByteArray protocolRawValue() - { - QByteArray ba; -#ifndef SRIVATSP -#else - for (int i=0; i < numFields(); i++) - ba.append(fieldRawValue(i)); -#endif - return ba; - } - virtual QString fieldName(int index) - { return QString("AbstractField"); } - virtual QString fieldTextValue(int index) - { return QString("AbstractFieldValue"); } - virtual QByteArray fieldRawValue(int index) - { return QByteArray(4, '\0'); } -}; - -class UnknownProtocol: public AbstractProtocol -{ - OstProto::Ack d; // FIXME(HI): replace 'Ack' with something else - -public: - virtual ~UnknownProtocol() {} - - virtual ::google::protobuf::Message& data() { return d; } - - virtual QString protocolName() - { return QString("UnknownProtocol"); } - QString protocolShortName() - { return QString("???Proto"); } - int numFields() - { return 1; } - QString fieldName(int index) - { return QString("UnknownField"); } - QString fieldTextValue(int index) - { return QString("UnknownFieldValue"); } - QByteArray fieldRawValue(int index) - { return QByteArray(); } -}; - -class PayloadProtocol: public AbstractProtocol -{ - Stream *parentStream; - OstProto::Ack d; // FIXME(HI): move payload related vars from - // stream into here - -public: - PayloadProtocol (Stream *stream = NULL) { parentStream = stream; } - virtual ~PayloadProtocol() {} - - virtual ::google::protobuf::Message& data() { return d; } - - virtual QString protocolName() - { return QString("Payload Data"); } - QString protocolShortName() - { return QString("DATA"); } - int numFields() - { return 1; } - QString fieldName(int index) - { return QString("Data"); } - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -class MacProtocol : public AbstractProtocol -{ -private: - OstProto::Mac d; - -public: - enum MacAddrMode { - MacAddrFixed, - MacAddrInc, - MacAddrDec - }; - virtual ~MacProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - - bool update(OstProto::Mac mac) { d.MergeFrom(mac); return true; } - - // Dst Mac - quint64 dstMac() - { return d.dst_mac(); } - - bool setDstMac(quint64 dstMac) - { d.set_dst_mac(dstMac); return true; } - - MacAddrMode dstMacMode() - { return (MacAddrMode) d.dst_mac_mode(); } - bool setDstMacMode(MacAddrMode dstMacMode) - { d.set_dst_mac_mode((OstProto::Mac::MacAddrMode)dstMacMode); return true; } - - quint16 dstMacCount() - { return d.dst_mac_count(); } - bool setDstMacCount(quint16 dstMacCount) - { d.set_dst_mac_count(dstMacCount); return true; } - - quint16 dstMacStep() - { return d.dst_mac_step(); } - bool setDstMacStep(quint16 dstMacStep) - { d.set_dst_mac_step(dstMacStep); return true; } - - // Src Mac - quint64 srcMac() - { return d.src_mac(); } - - bool setSrcMac(quint64 srcMac) - { d.set_src_mac(srcMac); return true; } - - MacAddrMode srcMacMode() - { return (MacAddrMode) d.src_mac_mode(); } - bool setSrcMacMode(MacAddrMode srcMacMode) - { d.set_src_mac_mode((OstProto::Mac::MacAddrMode)srcMacMode); return true; } - - quint16 srcMacCount() - { return d.src_mac_count(); } - bool setSrcMacCount(quint16 srcMacCount) - { d.set_src_mac_count(srcMacCount); return true; } - - quint16 srcMacStep() - { return d.src_mac_step(); } - bool setSrcMacStep(quint16 srcMacStep) - { d.set_src_mac_step(srcMacStep); return true; } - - - virtual QString protocolName() - { return QString("Media Access Control"); } - QString protocolShortName() - { return QString("MAC"); } - int numFields() - { return 2; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -class LlcProtocol : public AbstractProtocol -{ -private: - OstProto::Llc d; - -public: - virtual ~LlcProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - - bool update(OstProto::Llc llc) { d.MergeFrom(llc); return true; } - - quint8 dsap() - { return d.dsap(); } - bool setDsap(quint8 dsap) - { d.set_dsap(dsap); return true; } - - quint8 ssap() - { return d.ssap(); } - bool setSsap(quint8 ssap) - { d.set_ssap(ssap); return true; } - - quint8 ctl() - { return d.ctl(); } - bool setCtl(quint8 ctl) - { d.set_ctl(ctl); return true; } - - - virtual QString protocolName() - { return QString("802.3 Logical Link Control"); } - QString protocolShortName() - { return QString("LLC"); } - int numFields() - { return 3; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -class SnapProtocol : public AbstractProtocol -{ -private: - OstProto::Snap d; - -public: - virtual ~SnapProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Snap snap) { d.MergeFrom(snap); return true; } - - quint32 oui() - { return d.oui(); } - bool setOui(quint32 oui) - { d.set_oui(oui); return true; } - -// "Type" field: use from eth2 -#if 0 - quint16 type() - { return d.type(); } - bool setType(quint16 type) - { d.set_type(type); return true; } -#endif - virtual QString protocolName() - { return QString("SubNetwork Access Protocol"); } - QString protocolShortName() - { return QString("SNAP"); } - int numFields() - { return 1; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - - -class Eth2Protocol : public AbstractProtocol -{ -private: - OstProto::Eth2 d; - -public: - virtual ~Eth2Protocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Eth2 eth2) { d.MergeFrom(eth2); return true; } - - quint16 type() - { return d.type(); } - bool setType(quint16 type) - { d.set_type(type); return true; } - - - virtual QString protocolName() - { return QString("Protocol Type"); } - QString protocolShortName() - { return QString("TYPE"); } - int numFields() - { return 1; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - - -class Dot3Protocol : public AbstractProtocol -{ -private: - Stream *parentStream; - OstProto::Ack d; // FIXME(HI): replace 'ack' with somehting else - -public: - Dot3Protocol(Stream *stream = NULL) - { parentStream = stream; qDebug("parentStream = %p", stream); } - virtual ~Dot3Protocol() {} - virtual ::google::protobuf::Message& data() {return d;} - -#if 0 // FIXME(HI): remove? - quint16 length() - { return d.length(); } - bool setLength(quint16 length) - { return true; } -#endif - - virtual QString protocolName() - { return QString("802.3 Length"); } - QString protocolShortName() - { return QString("LEN"); } - int numFields() - { return 1; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -class VlanProtocol : public AbstractProtocol -{ - OstProto::Vlan d; -public: - virtual ~VlanProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Vlan vlan) { d.MergeFrom(vlan); return true; } - - enum VlanFlag { - VlanCvlanTagged = 0x01, - VlanCtpidOverride = 0x02, - VlanSvlanTagged = 0x04, - VlanStpidOverride = 0x08, - }; - Q_DECLARE_FLAGS(VlanFlags, VlanFlag); - - VlanFlags vlanFlags() - { - VlanFlags f; - - if (d.is_cvlan_tagged()) f|= VlanCvlanTagged; - if (d.is_ctpid_override()) f|= VlanCtpidOverride; - if (d.is_svlan_tagged()) f|= VlanSvlanTagged; - if (d.is_stpid_override()) f|= VlanStpidOverride; - - return f; - } - - bool setVlanFlags(VlanFlags vlanFlags) - { - d.set_is_cvlan_tagged(vlanFlags.testFlag(VlanCvlanTagged)); - d.set_is_ctpid_override(vlanFlags.testFlag(VlanCtpidOverride)); - d.set_is_svlan_tagged(vlanFlags.testFlag(VlanSvlanTagged)); - d.set_is_stpid_override(vlanFlags.testFlag(VlanStpidOverride)); - - return true; - } - - bool isUntagged() - { - if (!d.is_cvlan_tagged() && !d.is_svlan_tagged()) - return true; - else - return false; - } - - bool isSingleTagged() - { - if (( d.is_cvlan_tagged() && !d.is_svlan_tagged()) || - (!d.is_cvlan_tagged() && d.is_svlan_tagged()) ) - return true; - else - return false; - } - - bool isDoubleTagged() - { - if (d.is_cvlan_tagged() && d.is_svlan_tagged()) - return true; - else - return false; - } - - // CVLAN - quint16 ctpid() - { return d.ctpid(); } - bool setCtpid(quint16 ctpid) - { d.set_ctpid(ctpid); return true; } - - quint8 cvlanPrio() - { return (d.cvlan_tag() >> 13); } - bool setCvlanPrio(quint8 cvlanPrio) - { d.set_cvlan_tag((d.cvlan_tag() & 0x1FFF) | ((cvlanPrio & 0x3) << 13)); - return true; } - - quint8 cvlanCfi() - { return ((d.cvlan_tag() & 0x1000) >> 12); } - bool setCvlanCfi(quint8 cvlanCfi) - { d.set_cvlan_tag((d.cvlan_tag() & 0xEFFF) | ((cvlanCfi & 0x01) << 12)); - return true; } - - quint16 cvlanId() - { return (d.cvlan_tag() & 0x0FFF); } - bool setCvlanId(quint16 cvlanId) - { d.set_cvlan_tag((d.cvlan_tag() & 0xF000) | ((cvlanId & 0x0FFF))); - return true; } - - // SVLAN - quint16 stpid() - { return d.stpid(); } - bool setStpid(quint16 stpid) - { d.set_stpid(stpid); return true; } - quint8 svlanPrio() - { return (d.svlan_tag() >> 13); } - bool setSvlanPrio(quint8 svlanPrio) - { d.set_svlan_tag((d.svlan_tag() & 0x1FFF) | ((svlanPrio & 0x3) << 13)); - return true; } - - quint8 svlanCfi() - { return ((d.svlan_tag() & 0x1000) >> 12); } - bool setSvlanCfi(quint8 svlanCfi) - { d.set_svlan_tag((d.svlan_tag() & 0xEFFF) | ((svlanCfi & 0x01) << 12)); - return true; } - - quint16 svlanId() - { return (d.svlan_tag() & 0x0FFF); } - bool setSvlanId(quint16 svlanId) - { d.set_svlan_tag((d.svlan_tag() & 0xF000) | ((svlanId & 0x0FFF))); - return true; } - - virtual QString protocolName() - { return QString("Virtual Local Access Network"); } - QString protocolShortName() - { return QString("VLAN"); } - int numFields(); - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -// IP -class IpProtocol : public AbstractProtocol -{ -private: - OstProto::Ip d; - -public: - virtual ~IpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - - bool update(OstProto::Ip ip) { d.MergeFrom(ip); return true; } - - enum IpAddrMode { - IpAddrFixed, - IpAddrIncHost, - IpAddrDecHost, - IpAddrRandomHost - }; - - enum IpFlag { - IpOverrideVersion = 0x01, - IpOverrideHdrLen = 0x02, - IpOverrideTotLen = 0x04, - IpOverrideCksum = 0x08 - }; - Q_DECLARE_FLAGS(IpFlags, IpFlag); - - IpFlags ipFlags() - { - IpFlags f; - - if (d.is_override_ver()) f|= IpOverrideVersion; - if (d.is_override_hdrlen()) f|= IpOverrideHdrLen; - if (d.is_override_totlen()) f|= IpOverrideTotLen; - if (d.is_override_cksum()) f|= IpOverrideCksum; - - return f; - } - - bool setIpFlags(IpFlags ipFlags) - { - if (ipFlags.testFlag(IpOverrideVersion)) - d.set_is_override_ver(true); - else - d.set_is_override_ver(false); - - if (ipFlags.testFlag(IpOverrideHdrLen)) - d.set_is_override_hdrlen(true); - else - d.set_is_override_hdrlen(false); - - if (ipFlags.testFlag(IpOverrideTotLen)) - d.set_is_override_totlen(true); - else - d.set_is_override_totlen(false); - - if (ipFlags.testFlag(IpOverrideCksum)) - d.set_is_override_cksum(true); - else - d.set_is_override_cksum(false); - - return true; - } - - quint8 ver() - { return (d.ver_hdrlen() >> 4); } - bool setVer(quint8 ver) - { d.set_ver_hdrlen((d.ver_hdrlen() & 0x0F) | (ver << 4)); return true; } - - quint8 hdrLen() - { return (d.ver_hdrlen() & 0xF); } - bool setHdrLen(quint8 hdrLen) - { d.set_ver_hdrlen((d.ver_hdrlen() & 0xF0) | hdrLen); return true; } - - quint8 tos() - { return d.tos(); } - bool setTos(quint8 tos) - { d.set_tos(tos); return true; } - - quint16 totLen() - { return d.tot_len(); } - bool setTotLen(quint16 totLen) - { d.set_tot_len(totLen); return true; } - - quint16 id() - { return d.id(); } - bool setId(quint16 id) - { d.set_id(id); return true; } - - quint16 flags() - { return d.flags(); } - bool setFlags(quint16 flags) - { d.set_flags(flags); return true; } -#define IP_FLAG_UNUSED 0x1 -#define IP_FLAG_DF 0x2 -#define IP_FLAG_MF 0x4 - - quint16 fragOfs() - { return d.frag_ofs(); } - bool setFragOfs(quint16 fragOfs) - { d.set_frag_ofs(fragOfs); return true; } - - quint8 ttl() - { return d.ttl(); } - bool setTtl(quint8 ttl) - { d.set_ttl(ttl); return true; } - - quint8 proto() - { return d.proto(); } - bool setProto(quint8 proto) - { d.set_proto(proto); return true; } - - quint16 cksum() - { return d.cksum(); } - bool setCksum(quint16 cksum) - { d.set_cksum(cksum); return true; } - - // Source IP - quint32 srcIp() - { return d.src_ip(); } - bool setSrcIp(quint32 srcIp) - { d.set_src_ip(srcIp); return true; } - - IpAddrMode srcIpMode() - { return (IpAddrMode) d.src_ip_mode(); } - bool setSrcIpMode(IpAddrMode srcIpMode) - { d.set_src_ip_mode((OstProto::Ip::IpAddrMode)srcIpMode); return true; } - - quint16 srcIpCount() - { return d.src_ip_count(); } - bool setSrcIpCount(quint16 srcIpCount) - { d.set_src_ip_count(srcIpCount); return true; } - - quint32 srcIpMask() - { return d.src_ip_mask(); } - bool setSrcIpMask(quint32 srcIpMask) - { d.set_src_ip_mask(srcIpMask); return true; } - - // Destination IP - quint32 dstIp() - { return d.dst_ip(); } - bool setDstIp(quint32 dstIp) - { d.set_dst_ip(dstIp); return true; } - - IpAddrMode dstIpMode() - { return (IpAddrMode) d.dst_ip_mode(); } - bool setDstIpMode(IpAddrMode dstIpMode) - { d.set_dst_ip_mode((OstProto::Ip::IpAddrMode)dstIpMode); return true; } - - quint16 dstIpCount() - { return d.dst_ip_count(); } - bool setDstIpCount(quint16 dstIpCount) - { d.set_dst_ip_count(dstIpCount); return true; } - - quint32 dstIpMask() - { return d.dst_ip_mask(); } - bool setDstIpMask(quint32 dstIpMask) - { d.set_dst_ip_mask(dstIpMask); return true; } - - // TODO(LOW): Options - - - virtual QString protocolName() - { return QString("Internet Protocol version 4"); } - QString protocolShortName() - { return QString("IPv4"); } - int numFields() - { return 12; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(IpProtocol::IpFlags) - -class ArpProtocol: public AbstractProtocol -{ - // TODO(LOW): ARP - OstProto::Arp d; - -public: - virtual ~ArpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Arp arp) { d.MergeFrom(arp); return true; } -}; - -// TCP -class TcpProtocol : public AbstractProtocol -{ -private: - OstProto::Tcp d; - -public: - virtual ~TcpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - - bool update(OstProto::Tcp tcp) { d.MergeFrom(tcp); return true; } - - enum TcpFlag - { - TcpOverrideHdrLen = 0x01, - TcpOverrideCksum = 0x02 - }; - Q_DECLARE_FLAGS(TcpFlags, TcpFlag); - - TcpFlags tcpFlags() - { - TcpFlags f; - - if (d.is_override_hdrlen()) f|= TcpOverrideHdrLen; - if (d.is_override_cksum()) f|= TcpOverrideCksum; - - return f; - } - - bool setTcpFlags(TcpFlags tcpFlags) - { - if (tcpFlags.testFlag(TcpOverrideHdrLen)) - d.set_is_override_hdrlen(true); - else - d.set_is_override_hdrlen(false); - - if (tcpFlags.testFlag(TcpOverrideCksum)) - d.set_is_override_cksum(true); - else - d.set_is_override_cksum(false); - - return true; - } - - quint16 srcPort() - { return d.src_port(); } - bool setSrcPort(quint16 srcPort) - { d.set_src_port(srcPort); return true; } - - quint16 dstPort() - { return d.dst_port(); } - bool setDstPort(quint16 dstPort) - { d.set_dst_port(dstPort); return true; } - - quint32 seqNum() - { return d.seq_num(); } - bool setSeqNum(quint32 seqNum) - { d.set_seq_num(seqNum); return true;} - - quint32 ackNum() - { return d.ack_num(); } - bool setAckNum(quint32 ackNum) - { d.set_ack_num(ackNum); return true;} - - quint8 hdrLen() - { return (d.hdrlen_rsvd() >> 4); } - bool setHdrLen(quint8 hdrLen) - { d.set_hdrlen_rsvd((d.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); return true; } - - quint8 rsvd() - { return (d.hdrlen_rsvd() & 0xF); } - bool setRsvd(quint8 rsvd) - { d.set_hdrlen_rsvd((d.hdrlen_rsvd() & 0xF0) | rsvd); return true; } - - - // TODO(MED): convert to enum maybe? - quint8 flags() - { return d.flags(); } - bool setFlags(quint8 flags) - { d.set_flags(flags); return true; } -#define TCP_FLAG_URG 0x01 -#define TCP_FLAG_ACK 0x02 -#define TCP_FLAG_PSH 0x04 -#define TCP_FLAG_RST 0x08 -#define TCP_FLAG_SYN 0x10 -#define TCP_FLAG_FIN 0x20 - - quint16 window() - { return d.window(); } - bool setWindow(quint16 window) - { d.set_window(window); return true; } - - quint16 cksum() - { return d.cksum(); } - bool setCksum(quint16 cksum) - { d.set_cksum(cksum); return true; } - - quint16 urgPtr() - { return d.urg_ptr(); } - bool setUrgPtr(quint16 urg_ptr) - { d.set_urg_ptr(urg_ptr); return true; } - - - virtual QString protocolName() - { return QString("Transmission Control Protocol"); } - QString protocolShortName() - { return QString("TCP"); } - int numFields() - { return 10; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(TcpProtocol::TcpFlags) - - -// UDP -class UdpProtocol : public AbstractProtocol -{ -private: - OstProto::Udp d; - -public: - virtual ~UdpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - - bool update(OstProto::Udp udp) { d.MergeFrom(udp); return true; } - - enum UdpFlag - { - UdpOverrideTotLen = 0x01, - UdpOverrideCksum = 0x02 - }; - Q_DECLARE_FLAGS(UdpFlags, UdpFlag); - - UdpFlags udpFlags() - { - UdpFlags f; - - if (d.is_override_totlen()) f|= UdpOverrideTotLen; - if (d.is_override_cksum()) f|= UdpOverrideCksum; - - return f; - } - - bool setUdpFlags(UdpFlags udpFlags) - { - if (udpFlags.testFlag(UdpOverrideTotLen)) - d.set_is_override_totlen(true); - else - d.set_is_override_totlen(false); - - if (udpFlags.testFlag(UdpOverrideCksum)) - d.set_is_override_cksum(true); - else - d.set_is_override_cksum(false); - - return true; - } - - quint16 srcPort() - { return d.src_port(); } - bool setSrcPort(quint16 srcPort) - { d.set_src_port(srcPort); return true; } - - quint16 dstPort() - { return d.dst_port(); } - bool setDstPort(quint16 dstPort) - { d.set_dst_port(dstPort); return true; } - - quint16 totLen() - { return d.totlen(); } - bool setTotLen(quint16 totLen) - { d.set_totlen(totLen); return true; } - - quint16 cksum() - { return d.cksum(); } - bool setCksum(quint16 cksum) - { d.set_cksum(cksum); return true; } - - - virtual QString protocolName() - { return QString("User Datagram Protocol"); } - QString protocolShortName() - { return QString("UDP"); } - int numFields() - { return 4; } - QString fieldName(int index); - QString fieldTextValue(int index); - QByteArray fieldRawValue(int index); -}; - -class IcmpProtocol : public AbstractProtocol -{ -// TODO(LOW): ICMP - OstProto::Icmp d; - -public: - virtual ~IcmpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Icmp icmp) { d.MergeFrom(icmp); return true; } -}; - -class IgmpProtocol : public AbstractProtocol -{ -// TODO(LOW): IGMP - OstProto::Igmp d; - -public: - virtual ~IgmpProtocol() {} - virtual ::google::protobuf::Message& data() {return d;} - bool update(OstProto::Igmp igmp) { d.MergeFrom(igmp); return true; } -}; - - class Stream { quint32 mId; OstProto::StreamCore *mCore; OstProto::StreamControl *mControl; - UnknownProtocol *mUnknown; - PayloadProtocol *mPayload; - MacProtocol *mMac; - - LlcProtocol *mLlc; - SnapProtocol *mSnap; - Eth2Protocol *mEth2; - Dot3Protocol *mDot3; - - VlanProtocol *mVlan; - - IpProtocol *mIp; - ArpProtocol *mArp; - - TcpProtocol *mTcp; - UdpProtocol *mUdp; - IcmpProtocol *mIcmp; - IgmpProtocol *mIgmp; + QList mProtocolList; public: - //PayloadProtocol* Payload() { return mPayload; } - MacProtocol* mac() { return mMac; } void* core() { return mCore; } // FIXME(HI): Debug ONLY - - LlcProtocol* llc() { return mLlc; } - SnapProtocol* snap() { return mSnap; } - Eth2Protocol* eth2() { return mEth2; } - Dot3Protocol* dot3() { return mDot3; } - VlanProtocol* vlan() { return mVlan; } - - IpProtocol* ip() { return mIp; } - ArpProtocol* arp() { return mArp; } - - TcpProtocol* tcp() { return mTcp; } - UdpProtocol* udp() { return mUdp; } - IcmpProtocol* icmp() { return mIcmp; } - IgmpProtocol* igmp() { return mIgmp; } - + void loadProtocolWidgets(); + void storeProtocolWidgets(); public: enum FrameType { @@ -933,12 +85,15 @@ public: // Methods // ------------------------------------------------------- Stream(); + ~Stream(); + + void protoDataCopyFrom(Stream& stream); bool operator < (const Stream &s) const { return(mCore->ordinal() < s.mCore->ordinal()); } - void getConfig(uint portId, OstProto::Stream *s); + void getConfig(uint portId, OstProto::Stream &s); bool update(OstProto::Stream *stream); quint32 id() @@ -968,23 +123,6 @@ public: bool setName(QString name) { mCore->set_name(name.toStdString()); return true; } - FrameType frameType() - { return (FrameType) mCore->ft(); } - bool setFrameType(FrameType frameType) - { mCore->set_ft((OstProto::StreamCore::FrameType) frameType); return true; } - - // Data Pattern - DataPatternMode patternMode() - { return (DataPatternMode) mCore->pattern_mode(); } - bool setPatternMode(DataPatternMode patternMode) - { mCore->set_pattern_mode( - (OstProto::StreamCore::DataPatternMode) patternMode); return true; } - - quint32 pattern() - { return mCore->pattern(); } - bool setPattern(quint32 pattern) - { mCore->set_pattern(pattern); return true; } - // TODO(HI) : ????? #if 0 quint16 dataStartOfs; @@ -1012,18 +150,6 @@ public: bool setFrameLenMax(quint16 frameLenMax) { mCore->set_frame_len_max(frameLenMax); return true; } - L3Proto l3Proto() - { return (L3Proto) mCore->l3_proto(); } - bool setL3Proto(L3Proto l3Proto) - { mCore->set_l3_proto((OstProto::StreamCore::L3Proto) l3Proto); - return true; } - - L4Proto l4Proto() - { return (L4Proto) mCore->l4_proto(); } - bool setL4Proto(L4Proto l4Proto) - { mCore->set_l4_proto((OstProto::StreamCore::L4Proto) l4Proto); - return true; } - SendUnit sendUnit() { return (SendUnit) mControl->unit(); } bool setSendUnit(SendUnit sendUnit) @@ -1070,21 +196,12 @@ public: //--------------------------------------------------------------- // Methods for use by Packet Model //--------------------------------------------------------------- - - int numProtocols(); + QList frameProtocol(); + void setFrameProtocol(QList protocolList); //! Includes ALL protocol headers excluding payload data int protocolHeaderSize(); -#if 0 - int protocolId(int index); - int protocolIndex(int id); -#endif - AbstractProtocol* protocol(int index); -private: - QList selectedProtocols; - int mProtocolHeaderSize; - void updateSelectedProtocols(); - + AbstractProtocol* protocolById(int id); }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 8b99561..0079240 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -13,6 +13,11 @@ int StreamConfigDialog::lastProtoTabIndex = 0; StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) { + mCurrentStreamIndex = streamIndex; + + mpStream = new Stream; + mpStream->protoDataCopyFrom(*(mPort.streamByIndex(mCurrentStreamIndex))); + setupUi(this); setupUiExtra(); @@ -158,19 +163,15 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, rbFtNone->click(); #endif - //mpStreamList = streamList; - mCurrentStreamIndex = streamIndex; + //mmpStreamList = streamList; LoadCurrentStream(); - mpPacketModel = new PacketModel(&mPort.streamByIndex(mCurrentStreamIndex), - this); + mpPacketModel = new PacketModel(QList(), this); tvPacketTree->setModel(mpPacketModel); mpPacketModelTester = new ModelTest(mpPacketModel); tvPacketTree->header()->hide(); vwPacketDump->setModel(mpPacketModel); vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); - - // TODO(MED): //! \todo Enable navigation of streams pbPrev->setDisabled(true); @@ -180,7 +181,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); - // Finally, restore the saved last selected tab for the various tab widgets twTopLevel->setCurrentIndex(lastTopLevelTabIndex); if (twProto->isTabEnabled(lastProtoTabIndex)) @@ -193,6 +193,16 @@ void StreamConfigDialog::setupUiExtra() QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + // Add the Payload widget to the dialog + { + QGridLayout *layout; + + layout = static_cast(twTopLevel->widget(0)->layout()); + + layout->addWidget(mpStream->protocolById(52)->configWidget(), 0, 1); + qDebug("setupUi wgt = %p", mpStream->protocolById(52)->configWidget()); + } + // ---- Setup default stuff that cannot be done in designer ---- // Since the dialog defaults are FT = None, L3 = None, L4 = None; @@ -218,6 +228,7 @@ void StreamConfigDialog::setupUiExtra() lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); // L2 Ethernet +#if 0 // Proto FW leDstMac->setValidator(new QRegExpValidator(reMac, this)); leSrcMac->setValidator(new QRegExpValidator(reMac, this)); leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); @@ -225,6 +236,7 @@ void StreamConfigDialog::setupUiExtra() leCvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); leSvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); //leEtherType->setValidator(new QRegExpValidator(reHex2B, this)); +#endif /* ** Setup Connections @@ -244,31 +256,93 @@ StreamConfigDialog::~StreamConfigDialog() { delete mpPacketModelTester; delete mpPacketModel; + + // Remove payload data widget so that it is not deleted when this object + // is destroyed + { + QLayout *layout = twTopLevel->widget(0)->layout(); + if (layout) + { + qDebug("dstrct wgt = %p", mpStream->protocolById(52)->configWidget()); +#if 0 + int i = layout->indexOf(mpStream->protocolById(52)->configWidget()); + if (i >= 0) + { + layout->takeAt(i); + mpStream->protocolById(52)->configWidget()->setParent(0); + } +#endif + layout->removeWidget(mpStream->protocolById(52)->configWidget()); + mpStream->protocolById(52)->configWidget()->setParent(0); + + } + } + + // Remove any existing widget on the L2-L4 Tabs lest they are deleted + // when this object is destoryed + for (int i = 1; i <= 3; i++) + { + QLayout *layout = twProto->widget(i)->layout(); + if (layout) + { + QLayoutItem *child; + while ((child = layout->takeAt(0)) != 0) + { + Q_ASSERT(child->widget() != 0); + // Don't delete the child widget - reparent it + child->widget()->setParent(0); + } + delete layout; + } + } + + delete mpStream; } -void StreamConfigDialog::on_cmbPatternMode_currentIndexChanged(QString mode) +void StreamConfigDialog::updateSelectedProtocols() { - if (mode == "Fixed Word") + mSelectedProtocols.clear(); + + // FIXME: Hardcoded numbers! + + // Mac + mSelectedProtocols.append(51); + + if (rbFtEthernet2->isChecked()) + mSelectedProtocols.append(121); + else if (rbFt802Dot3Raw->isChecked()) + mSelectedProtocols.append(122); + else if (rbFt802Dot3Llc->isChecked()) { - lePattern->setEnabled(true); + mSelectedProtocols.append(122); + mSelectedProtocols.append(123); } - else if (mode == "Increment Byte") + else if (rbFtLlcSnap->isChecked()) { - lePattern->setDisabled(true); - } - else if (mode == "Decrement Byte") - { - lePattern->setDisabled(true); - } - if (mode == "Random") - { - lePattern->setDisabled(true); - } - else - { - qWarning("Unhandled/Unknown PatternMode = %s", mode.toAscii().data()); + mSelectedProtocols.append(122); + mSelectedProtocols.append(123); + mSelectedProtocols.append(124); } + + if (rbL3Ipv4->isChecked()) + mSelectedProtocols.append(130); + else if (rbL3Arp->isChecked()) + mSelectedProtocols.append(131); + + if (rbL4Tcp->isChecked()) + mSelectedProtocols.append(140); + else if (rbL4Udp->isChecked()) + mSelectedProtocols.append(141); + else if (rbL4Icmp->isChecked()) + mSelectedProtocols.append(142); + else if (rbL4Igmp->isChecked()) + mSelectedProtocols.append(143); + + // Payload + mSelectedProtocols.append(52); } + + void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { if (mode == "Fixed") @@ -301,63 +375,6 @@ void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) } } - -void StreamConfigDialog::on_cmbDstMacMode_currentIndexChanged(QString mode) -{ - if (mode == "Fixed") - { - leDstMacCount->setEnabled(false); - leDstMacStep->setEnabled(false); - } - else - { - leDstMacCount->setEnabled(true); - leDstMacStep->setEnabled(true); - } -} - -void StreamConfigDialog::on_cmbSrcMacMode_currentIndexChanged(QString mode) -{ - if (mode == "Fixed") - { - leSrcMacCount->setEnabled(false); - leSrcMacStep->setEnabled(false); - } - else - { - leSrcMacCount->setEnabled(true); - leSrcMacStep->setEnabled(true); - } -} - -void StreamConfigDialog::on_cmbIpSrcAddrMode_currentIndexChanged(QString mode) -{ - if (mode == "Fixed") - { - leIpSrcAddrCount->setDisabled(true); - leIpSrcAddrMask->setDisabled(true); - } - else - { - leIpSrcAddrCount->setEnabled(true); - leIpSrcAddrMask->setEnabled(true); - } -} - -void StreamConfigDialog::on_cmbIpDstAddrMode_currentIndexChanged(QString mode) -{ - if (mode == "Fixed") - { - leIpDstAddrCount->setDisabled(true); - leIpDstAddrMask->setDisabled(true); - } - else - { - leIpDstAddrCount->setEnabled(true); - leIpDstAddrMask->setEnabled(true); - } -} - void StreamConfigDialog::on_pbPrev_clicked() { #if 0 @@ -412,10 +429,13 @@ void StreamConfigDialog::on_rbL3Ipv4_toggled(bool checked) { if (checked) { - swL3Proto->setCurrentIndex(0); twProto->setTabEnabled(2, TRUE); twProto->setTabText(2, "L3 (IPv4)"); leType->setText("08 00"); + + // FIXME: Hardcoding + mpStream->protocolById(121)->setFieldData(0 /* type */, 0x800); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -428,10 +448,13 @@ void StreamConfigDialog::on_rbL3Arp_toggled(bool checked) { if (checked) { - swL3Proto->setCurrentIndex(1); twProto->setTabEnabled(2, TRUE); twProto->setTabText(2, "L3 (ARP)"); leType->setText("08 06"); + + // FIXME: Hardcoding + mpStream->protocolById(121)->setFieldData(0 /* type */, 0x806); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -446,10 +469,11 @@ void StreamConfigDialog::on_rbL4Icmp_toggled(bool checked) if (checked) { - swL4Proto->setCurrentIndex(2); twProto->setTabEnabled(3, TRUE); twProto->setTabText(3, "L4 (ICMP)"); - leIpProto->setText(uintToHexStr(IP_PROTO_ICMP, str, 1)); + // FIXME: Hardcoding + mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_ICMP); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -464,10 +488,11 @@ void StreamConfigDialog::on_rbL4Igmp_toggled(bool checked) if (checked) { - swL4Proto->setCurrentIndex(3); twProto->setTabEnabled(3, TRUE); twProto->setTabText(3, "L4 (IGMP)"); - leIpProto->setText(uintToHexStr(IP_PROTO_IGMP, str, 1)); + // FIXME: Hardcoding + mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_IGMP); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -482,10 +507,11 @@ void StreamConfigDialog::on_rbL4Tcp_toggled(bool checked) if (checked) { - swL4Proto->setCurrentIndex(0); twProto->setTabEnabled(3, TRUE); twProto->setTabText(3, "L4 (TCP)"); - leIpProto->setText(uintToHexStr(IP_PROTO_TCP, str, 1)); + // FIXME: Hardcoding + mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_TCP); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -500,10 +526,11 @@ void StreamConfigDialog::on_rbL4Udp_toggled(bool checked) if (checked) { - swL4Proto->setCurrentIndex(1); twProto->setTabEnabled(3, TRUE); twProto->setTabText(3, "L4 (UDP)"); - leIpProto->setText(uintToHexStr(IP_PROTO_UDP, str, 1)); + // FIXME: Hardcoding + mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_UDP); + mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param } else { @@ -512,6 +539,88 @@ void StreamConfigDialog::on_rbL4Udp_toggled(bool checked) } } +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + QList protoList; + + // We only process the "Packet View" tab + if (index != 2) + return; + + updateSelectedProtocols(); + foreach(int i, mSelectedProtocols) + if (mpStream->protocolById(i)) + protoList.append(mpStream->protocolById(i)); + + mpPacketModel->setSelectedProtocols(protoList); + StoreCurrentStream(mpStream); +} + +void StreamConfigDialog::on_twProto_currentChanged(int index) +{ + QLayout *layout; + QList wl; + + // We need to process only indices 1-3 i.e. the L2, L3 and L4 tabs + if ((index < 1) || (index > 3)) + return; + + // Remove any existing widget on the activated tab + layout = twProto->widget(index)->layout(); + if (layout) + { + QLayoutItem *child; + while ((child = layout->takeAt(0)) != 0) + { + Q_ASSERT(child->widget() != 0); + // Don't delete the child widget - reparent it + child->widget()->setParent(0); + } + delete layout; + } + + // FIXME: protocol id hardcodings + switch(index) + { + case 1: // L2 + wl.append(mpStream->protocolById(51)->configWidget()); + if (rbFtEthernet2->isChecked()) + wl.append(mpStream->protocolById(121)->configWidget()); + else if (rbFt802Dot3Raw->isChecked()) + wl.append(mpStream->protocolById(122)->configWidget()); + else if (rbFt802Dot3Llc->isChecked()) + { + wl.append(mpStream->protocolById(122)->configWidget()); + wl.append(mpStream->protocolById(123)->configWidget()); + } + else if (rbFtLlcSnap->isChecked()) + { + wl.append(mpStream->protocolById(122)->configWidget()); + wl.append(mpStream->protocolById(123)->configWidget()); + wl.append(mpStream->protocolById(124)->configWidget()); + } + break; + case 2: // L3 + if (rbL3Ipv4->isChecked()) + wl.append(mpStream->protocolById(130)->configWidget()); + break; + case 3: // L4 + if (rbL4Tcp->isChecked()) + wl.append(mpStream->protocolById(140)->configWidget()); + else if (rbL4Udp->isChecked()) + wl.append(mpStream->protocolById(141)->configWidget()); + break; + } + + if (wl.size()) + layout = new QVBoxLayout; + + for (int i=0; i < wl.size(); i++) + layout->addWidget(wl.at(i)); + + twProto->widget(index)->setLayout(layout); +} + void StreamConfigDialog::update_NumPacketsAndNumBursts() { if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) @@ -525,28 +634,6 @@ void StreamConfigDialog::update_NumPacketsAndNumBursts() leNumBursts->setEnabled(false); } -QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets) -{ - int i; - QChar zero('0'); - - hexStr = ""; - - for (i = octets; i > 0; i--) - { - ushort byte; - QString str1 = "%1"; - QString str; - - byte = num & 0xff; - str = str1.arg(byte, 2, 16, zero).append(' '); - hexStr.prepend(str); - num = num >> 8; - } - - return hexStr; -} - #if 0 void StreamConfigDialog::on_lePattern_editingFinished() { @@ -563,234 +650,133 @@ void StreamConfigDialog::on_lePattern_editingFinished() void StreamConfigDialog::LoadCurrentStream() { - Stream *pStream = &mPort.streamByIndex(mCurrentStreamIndex); QString str; - qDebug("loading pStream %p", pStream); + qDebug("loading mpStream %p", mpStream); // Meta Data { - cmbPatternMode->setCurrentIndex(pStream->patternMode()); - lePattern->setText(uintToHexStr(pStream->pattern(), str, 4)); - - cmbPktLenMode->setCurrentIndex(pStream->lenMode()); - lePktLen->setText(str.setNum(pStream->frameLen())); - lePktLenMin->setText(str.setNum(pStream->frameLenMin())); - lePktLenMax->setText(str.setNum(pStream->frameLenMax())); + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); } // Protocols { - qDebug("ft = %d\n", pStream->frameType()); - switch(pStream->frameType()) + int i; + + qDebug("Selected Protocols.size() = %d", mSelectedProtocols.size()); + mSelectedProtocols = mpStream->frameProtocol(); + qDebug("Selected Protocols.size() = %d", mSelectedProtocols.size()); + for (i = 0; i < mSelectedProtocols.size(); i++) + qDebug("%d: %d", i, mSelectedProtocols.at(i)); + + Q_ASSERT(mSelectedProtocols.size() >= 2); // Mac + Payload: mandatory + + i = 0; + Q_ASSERT(mSelectedProtocols.at(i) == 51); // Mac + i++; + + if (mSelectedProtocols.at(i) == 52) // Payload { - case Stream::e_ft_none: - rbFtNone->setChecked(TRUE); - break; - case Stream::e_ft_eth_2: - rbFtEthernet2->setChecked(TRUE); - break; - case Stream::e_ft_802_3_raw: - rbFt802Dot3Raw->setChecked(TRUE); - break; - case Stream::e_ft_802_3_llc: - rbFt802Dot3Llc->setChecked(TRUE); - break; - case Stream::e_ft_snap: - rbFtLlcSnap->setChecked(TRUE); - break; + i++; + goto _proto_parse_done; } - - leDsap->setText(uintToHexStr(pStream->llc()->dsap(), str, 1)); - leSsap->setText(uintToHexStr(pStream->llc()->ssap(), str, 1)); - leControl->setText(uintToHexStr(pStream->llc()->ctl(), str, 1)); - leOui->setText(uintToHexStr(pStream->snap()->oui(), str, 3)); - - leType->setText(uintToHexStr(pStream->eth2()->type(), str, 2)); - - switch(pStream->l3Proto()) + else if (mSelectedProtocols.at(i) == 121) // Eth2 { - case Stream::e_l3_none: - rbL3None->setChecked(true); - break; - case Stream::e_l3_ip: - rbL3Ipv4->setChecked(true); - break; - case Stream::e_l3_arp: - rbL3Arp->setChecked(true); - break; - default: - qDebug("%s: unknown L3 Protocol %d", __FUNCTION__, - pStream->l3Proto()); + rbFtEthernet2->setChecked(true); + i++; } - - switch(pStream->l4Proto()) + else if (mSelectedProtocols.at(i) == 122) // 802.3 RAW { - case Stream::e_l4_none: - rbL4None->setChecked(true); - break; - case Stream::e_l4_tcp: - rbL4Tcp->setChecked(true); - break; - case Stream::e_l4_udp: - rbL4Udp->setChecked(true); - break; - case Stream::e_l4_icmp: - rbL4Icmp->setChecked(true); - break; - case Stream::e_l4_igmp: - rbL4Igmp->setChecked(true); - break; - default: - qDebug("%s: unknown l4 Protocol %d", __FUNCTION__, - pStream->l4Proto()); - } - } - - // L2 - { - // L2 | Ethernet - { - leDstMac->setText(uintToHexStr(pStream->mac()->dstMac(), str, 6)); - cmbDstMacMode->setCurrentIndex(pStream->mac()->dstMacMode()); - leDstMacCount->setText(str.setNum(pStream->mac()->dstMacCount())); - leDstMacStep->setText(str.setNum(pStream->mac()->dstMacStep())); - - leSrcMac->setText(uintToHexStr(pStream->mac()->srcMac(), str, 6)); - cmbSrcMacMode->setCurrentIndex(pStream->mac()->srcMacMode()); - leSrcMacCount->setText(str.setNum(pStream->mac()->srcMacCount())); - leSrcMacStep->setText(str.setNum(pStream->mac()->srcMacStep())); - + if ((mSelectedProtocols.size() > (i+1)) && + (mSelectedProtocols.at(i+1) == 123)) // 802.3 LLC { - VlanProtocol *vlan = pStream->vlan(); - VlanProtocol::VlanFlags f; - - cmbCvlanPrio->setCurrentIndex(vlan->cvlanPrio()); - cmbCvlanCfi->setCurrentIndex(vlan->cvlanCfi()); - leCvlanId->setText(str.setNum(vlan->cvlanId())); - leCvlanTpid->setText(str.setNum(vlan->ctpid())); - cbCvlanTpidOverride->setChecked(vlan->vlanFlags().testFlag( - VlanProtocol::VlanCtpidOverride)); - gbCvlan->setChecked(vlan->vlanFlags().testFlag( - VlanProtocol::VlanCvlanTagged)); - - cmbSvlanPrio->setCurrentIndex(vlan->svlanPrio()); - cmbSvlanCfi->setCurrentIndex(vlan->svlanCfi()); - leSvlanId->setText(str.setNum(vlan->svlanId())); - leSvlanTpid->setText(str.setNum(vlan->stpid())); - cbSvlanTpidOverride->setChecked(vlan->vlanFlags().testFlag( - VlanProtocol::VlanStpidOverride)); - gbSvlan->setChecked(vlan->vlanFlags().testFlag( - VlanProtocol::VlanSvlanTagged)); + if ((mSelectedProtocols.size() > (i+2)) && + (mSelectedProtocols.at(i+2) == 124)) // SNAP + { + rbFtLlcSnap->setChecked(true); + i+=3; + } + else + { + rbFt802Dot3Llc->setChecked(true); + i+=2; + } + } + else + { + rbFt802Dot3Raw->setChecked(true); + i++; } } - } + else + rbFtNone->setChecked(true); - // L3 - { - // L3 | IP + // L3 + if (mSelectedProtocols.at(i) == 52) // Payload { - leIpVersion->setText(str.setNum(pStream->ip()->ver())); - cbIpVersionOverride->setChecked( - pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideVersion)); - leIpHdrLen->setText(str.setNum(pStream->ip()->hdrLen())); - cbIpHdrLenOverride->setChecked( - pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideHdrLen)); - - leIpTos->setText(uintToHexStr(pStream->ip()->tos(), str, 1)); - - leIpLength->setText(str.setNum(pStream->ip()->totLen())); - cbIpLengthOverride->setChecked( - pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideTotLen)); - - leIpId->setText(uintToHexStr(pStream->ip()->id(), str, 2)); - leIpFragOfs->setText(str.setNum(pStream->ip()->fragOfs())); - cbIpFlagsDf->setChecked((pStream->ip()->flags() & IP_FLAG_DF) > 0); - cbIpFlagsMf->setChecked((pStream->ip()->flags() & IP_FLAG_MF) > 0); - - leIpTtl->setText(str.setNum(pStream->ip()->ttl())); - leIpProto->setText(uintToHexStr(pStream->ip()->proto(), str, 1)); - - leIpCksum->setText(uintToHexStr(pStream->ip()->cksum(), str, 2)); - cbIpCksumOverride->setChecked( - pStream->ip()->ipFlags().testFlag(IpProtocol::IpOverrideCksum)); - - leIpSrcAddr->setText(QHostAddress(pStream->ip()->srcIp()).toString()); - cmbIpSrcAddrMode->setCurrentIndex(pStream->ip()->srcIpMode()); - leIpSrcAddrCount->setText(str.setNum(pStream->ip()->srcIpCount())); - leIpSrcAddrMask->setText(QHostAddress(pStream->ip()->srcIpMask()).toString()); - - leIpDstAddr->setText(QHostAddress(pStream->ip()->dstIp()).toString()); - cmbIpDstAddrMode->setCurrentIndex(pStream->ip()->dstIpMode()); - leIpDstAddrCount->setText(str.setNum(pStream->ip()->dstIpCount())); - leIpDstAddrMask->setText(QHostAddress(pStream->ip()->dstIpMask()).toString()); + i++; + goto _proto_parse_done; } - - // L3 | ARP + else if (mSelectedProtocols.at(i) == 130) // IP4 { - // TODO(LOW) + rbL3Ipv4->setChecked(true); + i++; } - } - - // L4 - { - // L4 | TCP + else if (mSelectedProtocols.at(i) == 131) // ARP { - leTcpSrcPort->setText(str.setNum(pStream->tcp()->srcPort())); - leTcpDstPort->setText(str.setNum(pStream->tcp()->dstPort())); - - leTcpSeqNum->setText(str.setNum(pStream->tcp()->seqNum())); - leTcpAckNum->setText(str.setNum(pStream->tcp()->ackNum())); - - leTcpHdrLen->setText(str.setNum(pStream->tcp()->hdrLen())); - cbTcpHdrLenOverride->setChecked((pStream->tcp()->tcpFlags(). - testFlag(TcpProtocol::TcpOverrideHdrLen))); - - leTcpWindow->setText(str.setNum(pStream->tcp()->window())); - - leTcpCksum->setText(str.setNum(pStream->tcp()->cksum())); - cbTcpCksumOverride->setChecked((pStream->tcp()->tcpFlags(). - testFlag(TcpProtocol::TcpOverrideCksum))); - - leTcpUrgentPointer->setText(str.setNum(pStream->tcp()->urgPtr())); - - cbTcpFlagsUrg->setChecked((pStream->tcp()->flags() & TCP_FLAG_URG) > 0); - cbTcpFlagsAck->setChecked((pStream->tcp()->flags() & TCP_FLAG_ACK) > 0); - cbTcpFlagsPsh->setChecked((pStream->tcp()->flags() & TCP_FLAG_PSH) > 0); - cbTcpFlagsRst->setChecked((pStream->tcp()->flags() & TCP_FLAG_RST) > 0); - cbTcpFlagsSyn->setChecked((pStream->tcp()->flags() & TCP_FLAG_SYN) > 0); - cbTcpFlagsFin->setChecked((pStream->tcp()->flags() & TCP_FLAG_FIN) > 0); + rbL3Arp->setChecked(true); + i++; } + else + rbL3None->setChecked(true); - // L4 | UDP + if (i == mSelectedProtocols.size()) + goto _proto_parse_done; + + // L4 + if (mSelectedProtocols.at(i) == 52) // Payload { - leUdpSrcPort->setText(str.setNum(pStream->udp()->srcPort())); - leUdpDstPort->setText(str.setNum(pStream->udp()->dstPort())); - - leUdpLength->setText(str.setNum(pStream->udp()->totLen())); - cbUdpLengthOverride->setChecked((pStream->udp()->udpFlags(). - testFlag(UdpProtocol::UdpOverrideTotLen))); - - - leUdpCksum->setText(str.setNum(pStream->udp()->cksum())); - cbUdpCksumOverride->setChecked((pStream->udp()->udpFlags(). - testFlag(UdpProtocol::UdpOverrideCksum))); + i++; + goto _proto_parse_done; } - - // L4 | ICMP + else if (mSelectedProtocols.at(i) == 140) // TCP { - // TODO(LOW) + rbL4Tcp->setChecked(true); + i++; } - - // L4 | IGMP + else if (mSelectedProtocols.at(i) == 141) // UDP { - // TODO(LOW) + rbL4Udp->setChecked(true); + i++; } + else if (mSelectedProtocols.at(i) == 142) // ICMP + { + rbL4Icmp->setChecked(true); + i++; + } + else if (mSelectedProtocols.at(i) == 143) // IGMP + { + rbL4Igmp->setChecked(true); + i++; + } + else + rbL4None->setChecked(true); + + Q_ASSERT(mSelectedProtocols.at(i) == 52); // Payload + i++; + +_proto_parse_done: + Q_ASSERT(i == mSelectedProtocols.size()); + + mpStream->loadProtocolWidgets(); } // Stream Control { - switch (pStream->sendUnit()) + switch (mpStream->sendUnit()) { case Stream::e_su_packets: rbSendPackets->setChecked(true); @@ -799,10 +785,10 @@ void StreamConfigDialog::LoadCurrentStream() rbSendBursts->setChecked(true); break; default: - qWarning("Unhandled sendUnit = %d\n", pStream->sendUnit()); + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); } - switch (pStream->sendMode()) + switch (mpStream->sendMode()) { case Stream::e_sm_fixed: rbModeFixed->setChecked(true); @@ -811,10 +797,10 @@ void StreamConfigDialog::LoadCurrentStream() rbModeContinuous->setChecked(true); break; default: - qWarning("Unhandled sendMode = %d\n", pStream->sendMode()); + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); } - switch(pStream->nextWhat()) + switch(mpStream->nextWhat()) { case Stream::e_nw_stop: rbActionStop->setChecked(true); @@ -826,31 +812,27 @@ void StreamConfigDialog::LoadCurrentStream() rbActionGotoStream->setChecked(true); break; default: - qWarning("Unhandled nextAction = %d\n", pStream->nextWhat()); + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); } - leNumPackets->setText(QString().setNum(pStream->numPackets())); - leNumBursts->setText(QString().setNum(pStream->numBursts())); - lePacketsPerBurst->setText(QString().setNum(pStream->burstSize())); - lePacketsPerSec->setText(QString().setNum(pStream->packetRate())); - leBurstsPerSec->setText(QString().setNum(pStream->burstRate())); + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); // TODO(MED): Change this when we support goto to specific stream leStreamId->setText(QString("0")); } } -void StreamConfigDialog::StoreCurrentStream() +void StreamConfigDialog::StoreCurrentStream(Stream *pStream) { - Stream *pStream = &mPort.streamByIndex(mCurrentStreamIndex); QString str; bool isOk; qDebug("storing pStream %p", pStream); // Meta Data - pStream->setPatternMode((Stream::DataPatternMode) cmbPatternMode->currentIndex()); - pStream->setPattern(lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); pStream->setFrameLen(lePktLen->text().toULong(&isOk)); pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); @@ -858,235 +840,9 @@ void StreamConfigDialog::StoreCurrentStream() // Protocols { - if (rbFtNone->isChecked()) - pStream->setFrameType(Stream::e_ft_none); - else if (rbFtEthernet2->isChecked()) - pStream->setFrameType(Stream::e_ft_eth_2); - else if (rbFt802Dot3Raw->isChecked()) - pStream->setFrameType(Stream::e_ft_802_3_raw); - else if (rbFt802Dot3Llc->isChecked()) - pStream->setFrameType(Stream::e_ft_802_3_llc); - else if (rbFtLlcSnap->isChecked()) - pStream->setFrameType(Stream::e_ft_snap); - qDebug("store ft(%d)\n", pStream->frameType()); - - pStream->llc()->setDsap(leDsap->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->llc()->setSsap(leSsap->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->llc()->setCtl(leControl->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->snap()->setOui(leOui->text().remove(QChar(' ')).toULong(&isOk, 16)); - pStream->eth2()->setType(leType->text().remove(QChar(' ')).toULong(&isOk, 16)); - - if (rbL3None->isChecked()) - pStream->setL3Proto(Stream::e_l3_none); - else if (rbL3Ipv4->isChecked()) - pStream->setL3Proto(Stream::e_l3_ip); - else if (rbL3Arp->isChecked()) - pStream->setL3Proto(Stream::e_l3_arp); - else - { - qCritical("No L3 Protocol??? Problem in Code!!!"); - pStream->setL3Proto(Stream::e_l3_none); - } - - if (rbL4None->isChecked()) - pStream->setL4Proto(Stream::e_l4_none); - else if (rbL4Tcp->isChecked()) - pStream->setL4Proto(Stream::e_l4_tcp); - else if (rbL4Udp->isChecked()) - pStream->setL4Proto(Stream::e_l4_udp); - else if (rbL4Icmp->isChecked()) - pStream->setL4Proto(Stream::e_l4_icmp); - else if (rbL4Igmp->isChecked()) - pStream->setL4Proto(Stream::e_l4_igmp); - else - { - qCritical("No L4 Protocol??? Problem in Code!!!"); - pStream->setL4Proto(Stream::e_l4_none); - } - } - - // L2 - { - // L2 | Ethernet - { - qDebug("%s: LL dstMac = %llx", __FUNCTION__, - leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); - pStream->mac()->setDstMac( - leDstMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); -#if 1 - qDebug("%s: dstMac = %llx", __FUNCTION__, - pStream->mac()->dstMac()); - qDebug("%s: dstMac = [%s] %d", __FUNCTION__, - leDstMac->text().toAscii().constData(), isOk); -#endif - pStream->mac()->setDstMacMode( - (MacProtocol::MacAddrMode) cmbDstMacMode->currentIndex()); - pStream->mac()->setDstMacCount( - leDstMacCount->text().toULong(&isOk)); - pStream->mac()->setDstMacStep( - leDstMacStep->text().toULong(&isOk)); - - pStream->mac()->setSrcMac( - leSrcMac->text().remove(QChar(' ')).toULongLong(&isOk, 16)); - qDebug("%s: srcMac = %llx", __FUNCTION__, - pStream->mac()->srcMac()); - qDebug("%s: srcMac = [%s] %d", __FUNCTION__, - leSrcMac->text().toAscii().constData(), isOk); - pStream->mac()->setSrcMacMode( - (MacProtocol::MacAddrMode) cmbSrcMacMode->currentIndex()); - pStream->mac()->setSrcMacCount( - leSrcMacCount->text().toULong(&isOk)); - pStream->mac()->setSrcMacStep( - - leSrcMacStep->text().toULong(&isOk)); - - { - VlanProtocol *vlan = pStream->vlan(); - VlanProtocol::VlanFlags f = 0; - - vlan->setCvlanPrio(cmbCvlanPrio->currentIndex()); - vlan->setCvlanCfi(cmbCvlanCfi->currentIndex()); - vlan->setCvlanId(leCvlanId->text().toULong(&isOk)); - vlan->setCtpid(leCvlanTpid->text().remove(QChar(' ')).toULong(&isOk, 16)); - if (cbCvlanTpidOverride->isChecked()) - f |= VlanProtocol::VlanCtpidOverride; - if (gbCvlan->isChecked()) - f |= VlanProtocol::VlanCvlanTagged; - - vlan->setSvlanPrio(cmbSvlanPrio->currentIndex()); - vlan->setSvlanCfi(cmbSvlanCfi->currentIndex()); - vlan->setSvlanId(leSvlanId->text().toULong(&isOk)); - vlan->setStpid(leSvlanTpid->text().remove(QChar(' ')).toULong(&isOk, 16)); - if (cbSvlanTpidOverride->isChecked()) - f |= VlanProtocol::VlanStpidOverride; - if (gbSvlan->isChecked()) - f |= VlanProtocol::VlanSvlanTagged; - - vlan->setVlanFlags(f); - } - } - } - - // L3 - { - // L3 | IP - { - IpProtocol *ip = pStream->ip(); - IpProtocol::IpFlags f = 0; - int ff = 0; - - ip->setVer(leIpVersion->text().toULong(&isOk)); - if (cbIpVersionOverride->isChecked()) - f |= IpProtocol::IpOverrideVersion; - ip->setHdrLen(leIpHdrLen->text().toULong(&isOk)); - if (cbIpHdrLenOverride->isChecked()) - f |= IpProtocol::IpOverrideHdrLen; - - ip->setTos(leIpTos->text().toULong(&isOk, 16)); - - ip->setTotLen(leIpLength->text().toULong(&isOk)); - if (cbIpLengthOverride->isChecked()) - f |= IpProtocol::IpOverrideHdrLen; - - ip->setId(leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); - ip->setFragOfs(leIpFragOfs->text().toULong(&isOk)); - - if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; - if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; - ip->setFlags(ff); - - ip->setTtl(leIpTtl->text().toULong(&isOk)); - ip->setProto(leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); - - ip->setCksum(leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); - if (cbIpCksumOverride->isChecked()) - f |= IpProtocol::IpOverrideCksum; - - ip->setSrcIp(QHostAddress(leIpSrcAddr->text()).toIPv4Address()); - ip->setSrcIpMode((IpProtocol::IpAddrMode) cmbIpSrcAddrMode->currentIndex()); - ip->setSrcIpCount(leIpSrcAddrCount->text().toULong(&isOk)); - ip->setSrcIpMask(QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); - - ip->setDstIp(QHostAddress(leIpDstAddr->text()).toIPv4Address()); - ip->setDstIpMode((IpProtocol::IpAddrMode) cmbIpDstAddrMode->currentIndex()); - ip->setDstIpCount(leIpDstAddrCount->text().toULong(&isOk)); - ip->setDstIpMask(QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); - - ip->setIpFlags(f); - } - - // L3 | ARP - { - // TODO(LOW) - } - } - - // L4 - { - // L4 | TCP - { - TcpProtocol *tcp = pStream->tcp(); - TcpProtocol::TcpFlags f = 0; - int ff = 0; - - tcp->setSrcPort(leTcpSrcPort->text().toULong(&isOk)); - tcp->setDstPort(leTcpDstPort->text().toULong(&isOk)); - - tcp->setSeqNum(leTcpSeqNum->text().toULong(&isOk)); - tcp->setAckNum(leTcpAckNum->text().toULong(&isOk)); - - tcp->setHdrLen(leTcpHdrLen->text().toULong(&isOk)); - if (cbTcpHdrLenOverride->isChecked()) - f |= TcpProtocol::TcpOverrideHdrLen; - - tcp->setWindow(leTcpWindow->text().toULong(&isOk)); - - tcp->setCksum(leTcpCksum->text().remove(QChar(' ')).toULong(&isOk)); - if (cbTcpCksumOverride->isChecked()) - f |= TcpProtocol::TcpOverrideCksum; - - tcp->setUrgPtr(leTcpUrgentPointer->text().toULong(&isOk)); - - if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; - if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; - if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; - if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; - if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; - if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; - tcp->setFlags(ff); - - tcp->setTcpFlags(f); - } - - // L4 | UDP - { - UdpProtocol *udp = pStream->udp(); - UdpProtocol::UdpFlags f = 0; - - udp->setSrcPort(leUdpSrcPort->text().toULong(&isOk)); - udp->setDstPort(leUdpDstPort->text().toULong(&isOk)); - - udp->setTotLen(leUdpLength->text().toULong(&isOk)); - - if (cbUdpLengthOverride->isChecked()) - f |= UdpProtocol::UdpOverrideTotLen; - - udp->setCksum(leUdpCksum->text().remove(QChar(' ')).toULong(&isOk)); - if (cbUdpCksumOverride->isChecked()) - f |= UdpProtocol::UdpOverrideCksum; - - udp->setUdpFlags(f); - } - - // L4 | ICMP - { - // TODO)(LOW) - } - - // L4 | IGMP - { - // TODO(LOW) - } + updateSelectedProtocols(); + pStream->setFrameProtocol(mSelectedProtocols); + pStream->storeProtocolWidgets(); } // Stream Control @@ -1119,11 +875,9 @@ void StreamConfigDialog::StoreCurrentStream() void StreamConfigDialog::on_pbOk_clicked() { // Store dialog contents into stream - StoreCurrentStream(); + StoreCurrentStream(mPort.streamByIndex(mCurrentStreamIndex)); qDebug("stream stored"); lastTopLevelTabIndex = twTopLevel->currentIndex(); lastProtoTabIndex = twProto->currentIndex(); } -//Junk Line for introducing a compiler error - diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 7dbedbe..f6c5a41 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -14,7 +14,7 @@ /* ** TODO -** - Improve HexStr handling +** \todo Improve HexStr handling ** */ @@ -31,9 +31,13 @@ private: Port& mPort; uint mCurrentStreamIndex; + Stream *mpStream; + QList mSelectedProtocols; + PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; + // The following static variables are used to track the "selected" tab // for the various tab widgets so that it can be restored when the dialog // is opened the next time @@ -41,16 +45,12 @@ private: static int lastProtoTabIndex; void setupUiExtra(); + void updateSelectedProtocols(); void LoadCurrentStream(); - void StoreCurrentStream(); + void StoreCurrentStream(Stream *pStream); private slots: - void on_cmbPatternMode_currentIndexChanged(QString mode); void on_cmbPktLenMode_currentIndexChanged(QString mode); - void on_cmbDstMacMode_currentIndexChanged(QString mode); - void on_cmbSrcMacMode_currentIndexChanged(QString mode); - void on_cmbIpSrcAddrMode_currentIndexChanged(QString mode); - void on_cmbIpDstAddrMode_currentIndexChanged(QString mode); void on_pbPrev_clicked(); void on_pbNext_clicked(); @@ -59,21 +59,20 @@ private slots: void on_rbFt802Dot3Raw_toggled(bool checked); void on_rbFt802Dot3Llc_toggled(bool checked); void on_rbFtLlcSnap_toggled(bool checked); - void on_rbL3Ipv4_toggled(bool checked); void on_rbL3Arp_toggled(bool checked); - void on_rbL4Icmp_toggled(bool checked); void on_rbL4Igmp_toggled(bool checked); void on_rbL4Tcp_toggled(bool checked); void on_rbL4Udp_toggled(bool checked); + void on_twTopLevel_currentChanged(int index); + void on_twProto_currentChanged(int index); + void update_NumPacketsAndNumBursts(); void on_pbOk_clicked(); }; -QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); - #endif diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 70fea95..e410c81 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,13 +8,16 @@ 0 0 - 534 - 521 + 590 + 517 Edit Stream + + :/icons/stream_edit.png + QLineEdit:enabled[inputMask = "HH; "], QLineEdit:enabled[inputMask = "HH HH; "], @@ -35,7 +38,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 - + Packet Config @@ -53,57 +56,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Data Pattern - - - - - - - Fixed Word - - - - - Increment Byte - - - - - Decrement Byte - - - - - Random - - - - - - - - HH HH HH HH; - - - - - - 11 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + Frame Length (including CRC) @@ -494,1198 +448,24 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff L2 - - - - - Ethernet - - - - - - Destination - - - - - - - HH HH HH HH HH HH; - - - - - - - - - - - Fixed - - - - - Increment - - - - - Decrement - - - - - - - - Count - - - - - - - false - - - 1 - - - 1 - - - - - - - Step - - - - - - - false - - - 1 - - - 1 - - - - - - - Source - - - - - - - HH HH HH HH HH HH; - - - - - - - - - - - Fixed - - - - - Increment - - - - - Decrement - - - - - - - - Count - - - - - - - false - - - 1 - - - - - - - Step - - - - - - - false - - - 1 - - - 1 - - - - - - - - - - VLAN/CVLAN - - - true - - - false - - - - - - Priority - - - - - - - CFI - - - - - - - VLAN - - - - - - - false - - - Override TPID - - - - - - - false - - - - 0 - - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 - - - - - - - - false - - - - 0 - - - - - 1 - - - - - - - - false - - - 0 - - - - - - - false - - - HH HH; - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - SVLAN - - - true - - - false - - - - - - Priority - - - - - - - CFI - - - - - - - VLAN - - - - - - - false - - - Override TPID - - - - - - - false - - - - 0 - - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 - - - - - - - - false - - - - 0 - - - - - 1 - - - - - - - - false - - - 0 - - - - - - - false - - - HH HH; - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + L3 - - - - - 0 - - - - - - - - - Override Version - - - - - - - false - - - 4 - - - - - - - Override Header Length - - - - - - - false - - - 5 - - - - - - - TOS/DSCP - - - - - - - HH; - - - - - - - - - - false - - - ... - - - - - - - Override Length - - - - - - - false - - - - - - - Identification - - - - - - - HH HH; - - - - - - - - - Qt::Vertical - - - - - - - - - Fragment Offset - - - - - - - - - - Don't Fragment - - - - - - - More Fragments - - - - - - - Time To Live (TTL) - - - - - - - 64 - - - - - - - Protocol - - - - - - - false - - - - - - - - - - Override Checksum - - - - - - - false - - - HH HH; - - - - - - - - - - - - false - - - - - - Mode - - - - - - - Count - - - - - - - Mask - - - - - - - Source - - - - - - - 009.009.009.009; - - - ... - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - Fixed - - - - - Increment Host - - - - - Decrement Host - - - - - Random Host - - - - - - - - false - - - - - - - - - - false - - - 255.255.255.255 - - - - - - - Destination - - - - - - - 000.000.000.000; - - - ... - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - Fixed - - - - - Increment Host - - - - - Decrement Host - - - - - Random Host - - - - - - - - false - - - - - - - - - - false - - - 255.255.255.255 - - - - - - - - - - - - Options - - - - - - - false - - - TODO - - - - - - - false - - - ... - - - - - - - - - Qt::Vertical - - - - 469 - 16 - - - - - - - - - - - 105 - 100 - 296 - 76 - - - - ARP : TODO - - - Qt::AlignCenter - - - - - - L4 - - - - - 0 - - - - - - - - - Source Port - - - - - - - - - - Destination Port - - - - - - - - - - Sequence Number - - - - - - - - - - Acknowledgement Number - - - - - - - - - - Override Header Length (x4) - - - - - - - false - - - - - - - Window - - - - - - - - - - Override Checksum - - - - - - - false - - - HH HH; - - - - - - - Urgent Pointer - - - - - - - - - - - - Qt::Vertical - - - - - - - Flags - - - - - - URG - - - - - - - ACK - - - - - - - PSH - - - - - - - RST - - - - - - - SYN - - - - - - - FIN - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 181 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - Source Port - - - - - - - - - - Destination Port - - - - - - - - - - Override Length - - - - - - - false - - - - - - - Override Checksum - - - - - - - false - - - HH HH; - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - ICMP: TODO - - - - - - - - - - - IGMP: TODO - - - - - - - - + - + Stream Control @@ -2042,7 +822,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Packet View @@ -2118,11 +898,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - HexLineEdit - QLineEdit -
hexlineedit.h
-
DumpView QWidget @@ -2132,326 +907,16 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
twTopLevel - cmbPatternMode - lePattern cmbPktLenMode lePktLen lePktLenMin lePktLenMax twProto - leDstMac - cmbDstMacMode - leDstMacCount - leDstMacStep - leSrcMac - cmbSrcMacMode - leSrcMacCount - leSrcMacStep - gbCvlan - cmbCvlanPrio - cmbCvlanCfi - leCvlanId - cbCvlanTpidOverride - leCvlanTpid - gbSvlan - cmbSvlanPrio - cmbSvlanCfi - leSvlanId - cbSvlanTpidOverride - leSvlanTpid - - cbCvlanTpidOverride - toggled(bool) - leCvlanTpid - setEnabled(bool) - - - 112 - 267 - - - 112 - 267 - - - - - cbSvlanTpidOverride - toggled(bool) - leSvlanTpid - setEnabled(bool) - - - 112 - 267 - - - 112 - 267 - - - - - gbCvlan - toggled(bool) - cmbCvlanPrio - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - gbCvlan - toggled(bool) - cmbCvlanCfi - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - gbCvlan - toggled(bool) - leCvlanId - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - gbCvlan - toggled(bool) - cbCvlanTpidOverride - setEnabled(bool) - - - 92 - 267 - - - 112 - 267 - - - - - gbSvlan - toggled(bool) - cmbSvlanPrio - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - gbSvlan - toggled(bool) - leSvlanId - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - gbSvlan - toggled(bool) - cbSvlanTpidOverride - setEnabled(bool) - - - 92 - 267 - - - 112 - 267 - - - - - gbSvlan - toggled(bool) - cmbSvlanCfi - setEnabled(bool) - - - 92 - 267 - - - 92 - 267 - - - - - cbUdpLengthOverride - toggled(bool) - leUdpLength - setEnabled(bool) - - - 145 - 334 - - - 145 - 334 - - - - - cbUdpCksumOverride - toggled(bool) - leUdpCksum - setEnabled(bool) - - - 145 - 334 - - - 145 - 334 - - - - - cbIpVersionOverride - toggled(bool) - leIpVersion - setEnabled(bool) - - - 123 - 305 - - - 123 - 305 - - - - - cbIpHdrLenOverride - toggled(bool) - leIpHdrLen - setEnabled(bool) - - - 123 - 305 - - - 123 - 305 - - - - - cbIpLengthOverride - toggled(bool) - leIpLength - setEnabled(bool) - - - 123 - 305 - - - 123 - 305 - - - - - cbIpCksumOverride - toggled(bool) - leIpCksum - setEnabled(bool) - - - 192 - 305 - - - 192 - 305 - - - - - cbTcpHdrLenOverride - toggled(bool) - leTcpHdrLen - setEnabled(bool) - - - 145 - 334 - - - 145 - 334 - - - - - cbTcpCksumOverride - toggled(bool) - leTcpCksum - setEnabled(bool) - - - 145 - 334 - - - 145 - 334 - - - pbOk clicked() @@ -2459,11 +924,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff accept() - 460 - 510 + 440 + 466 - 565 + 533 433 @@ -2475,11 +940,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff reject() - 543 - 510 + 523 + 466 - 561 + 533 466 @@ -2491,12 +956,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 136 - 120 + 281 + 137 - 136 - 120 + 281 + 169 @@ -2507,12 +972,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 41 - 101 + 30 + 66 - 96 - 120 + 30 + 266 @@ -2523,12 +988,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 41 - 120 + 30 + 91 - 78 - 120 + 30 + 312 @@ -2539,12 +1004,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 41 - 120 + 30 + 91 - 136 - 120 + 134 + 177 diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 6680f58..c1b887c 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -82,7 +82,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const case StreamName: { if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->streamByIndex(index.row()).name(); + return mCurrentPort->streamByIndex(index.row())->name(); else return QVariant(); break; @@ -91,7 +91,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const { if ((role == Qt::CheckStateRole)) { - if (mCurrentPort->streamByIndex(index.row()).isEnabled()) + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) return Qt::Checked; else return Qt::Unchecked; @@ -102,7 +102,7 @@ QVariant StreamModel::data(const QModelIndex &index, int role) const } case StreamNextWhat: { - int val = mCurrentPort->streamByIndex(index.row()).nextWhat(); + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); if (role == Qt::DisplayRole) return nextWhatOptionList().at(val); @@ -131,19 +131,19 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r { // Edit Supported Fields case StreamName: - mCurrentPort->streamByIndex(index.row()).setName(value.toString()); + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); emit(dataChanged(index, index)); return true; case StreamStatus: - mCurrentPort->streamByIndex(index.row()).setIsEnabled(value.toBool()); + mCurrentPort->streamByIndex(index.row())->setIsEnabled(value.toBool()); emit(dataChanged(index, index)); return true; case StreamNextWhat: if (role == Qt::EditRole) { - mCurrentPort->streamByIndex(index.row()).setNextWhat( + mCurrentPort->streamByIndex(index.row())->setNextWhat( (Stream::NextWhat)value.toInt()); emit(dataChanged(index, index)); return true; diff --git a/common/Makefile b/common/Makefile deleted file mode 100644 index 7c95f2b..0000000 --- a/common/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -RM=del -PROTOC=protoc - -#-#-# - -all: protocol.pb.cc - -protocol.pb.cc: protocol.proto - $(PROTOC) --cpp_out=. $< - -clean: - $(RM) *.pb.h *.pb.cc - -distclean: clean diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..429c6a3 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,225 @@ +#include "abstractprotocol.h" + +/*! + \class AbstractProtocol + + // FIXME - update this text + Bare Minimum set of methods that a subclass needs to reimplement + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + + Any useful protocol should also provide implementations for + - name() + - shortName() + - fieldName() + + Protocols with meta fields should additionally implement + - metaFieldCount() + - isMetaField() +*/ +AbstractProtocol::AbstractProtocol(Stream *parent) +{ + stream = parent; + metaCount = -1; +} + +AbstractProtocol::~AbstractProtocol() +{ +} + + +/*! + \fn virtual void protoDataCopyInto(OstProto::Stream &stream) = 0; + + Copy the protocol's protobuf into the passed in stream \n + In the base class this is a pure virtual function. Subclasses should + implement this function by using - \n + stream.AddExtension()->CopyFrom() */ + +/* + \fn virtual void protoDataCopyFrom(const OstProto::Stream &stream) = 0; + FIXME */ + +/*! Returns the full name of the protocol \n + The default implementation returns a null string */ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! Returns the short name or abbreviation of the protocol \n + The default implementation forms and returns a abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation */ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! Returns the number of fields (both Frame and Meta fields) \n + The default implementation returns zero */ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! Returns the number of meta fields \n + The default implementation counts and returns the number of fields for which + fieldData(index, FieldIsMeta) return true\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count */ +int AbstractProtocol::metaFieldCount() const +{ + if (metaCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldData(i, FieldIsMeta).toBool()) + c++; + metaCount = c; + } + + return metaCount; +} + +/*! Returns the number of frame fields \n + Convenience method - same as fieldCount() minus metaFieldCount() */ +int AbstractProtocol::frameFieldCount() const +{ + //qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount()); + return (fieldCount() - metaFieldCount()); +} + +/*! Returns the requested field attribute data \n + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attriubutes e.g. FieldName may be invariant across streams\n + The default implementation returns the fieldValue() converted to string + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + The default implementation returns FIXME + */ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + case FieldIsMeta: + return false; + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! Sets the value of a field corresponding to index \n + Returns true if field is successfully set, false otherwise \n + The default implementation always returns false */ +bool AbstractProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + return false; +} + +/*! Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol taking care of fields + which are not an integral number of bytes\n */ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex) const +{ + QByteArray proto, field; + int bits, lastbitpos = 0; + + for (int i=0; i < fieldCount() ; i++) + { + if (!fieldData(i, FieldIsMeta).toBool()) + { + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + + qDebug("<<< %d, %d >>>>", proto.size(), field.size()); + + if (bits == field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + field.at(j+1) >> lastbitpos); + } + } + else if (bits < field.size() * 8) + { + int u, v; + + u = bits / 8; + v = bits % 8; + if (lastbitpos == 0) + { + proto.append(field.left(u+1)); + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = c & (0xFF << (8 - v)); + lastbitpos = v; + } + else + { + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); + for (int j = 0; j < (u - 1); j++) + proto.append(field.at(j) << lastbitpos | + field.at(j+1) >> lastbitpos); + if (u) + proto.append( field.at(u) & (0xFF << (8 - v)) ); + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..7c7255e --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,62 @@ +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include + +#include "../common/protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +class Stream; + +class AbstractProtocol +{ +private: + mutable int metaCount; + mutable QString protoAbbr; + +protected: + Stream *stream; + +public: + enum FieldAttrib { + FieldName, //! name + FieldValue, //! value in host byte order (user editable) + FieldTextValue, //! value as text + FieldFrameValue, //! frame encoded value in network byte order + FieldBitSize, //! size in bits + FieldIsMeta //! bool indicating if field is meta + }; + + AbstractProtocol(Stream *parent = 0); + virtual ~AbstractProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream) = 0; + virtual void protoDataCopyFrom(const OstProto::Stream &stream) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int metaFieldCount() const; + int frameFieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; +}; + +#endif diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..67b70d9 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,110 @@ +#include +#include + +#include "Dot3.h" + +Dot3ConfigForm *Dot3Protocol::configForm = NULL; + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Dot3Protocol::Dot3Protocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new Dot3ConfigForm; +} + +Dot3Protocol::~Dot3Protocol() +{ +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::dot3)->CopyFrom(data); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::dot3)) + data.MergeFrom(stream.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + return data.length(); + case FieldTextValue: + return QString("%1").arg(data.length()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.length(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* Dot3Protocol::configWidget() +{ + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configForm->leLength->setText(QString().setNum(data.length())); +} + +void Dot3Protocol::storeConfigWidget() +{ + bool isOk; + + data.set_length(configForm->leLength->text().toULong(&isOk)); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..e49f312 --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,50 @@ +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_Dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + static Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(Stream *parent = 0); + virtual ~Dot3Protocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..37f78ca --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,12 @@ +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional uint32 length = 1; +} + +extend Stream { + optional Dot3 dot3 = 122; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..d452bd0 --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,59 @@ + + dot3 + + + + 0 + 0 + 131 + 72 + + + + Form + + + + + + 802.3 + + + + + + Length + + + leLength + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..caaf75b --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include "eth2.h" + +Eth2ConfigForm *Eth2Protocol::configForm = NULL; + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new Eth2ConfigForm; +} + +Eth2Protocol::~Eth2Protocol() +{ +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::eth2)->CopyFrom(data); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::eth2)) + data.MergeFrom(stream.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return data.type(); + case FieldTextValue: + return QString("%1").arg(data.type(), 16); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + } + default: + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + configForm->leType->setText(uintToHexStr(data.type(), 2)); + +#undef uintToHexStr +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..580b712 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,50 @@ +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + static Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_fieldCount + }; + +public: + Eth2Protocol(Stream *parent = 0); + virtual ~Eth2Protocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..224c25d --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,12 @@ +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional uint32 type = 1; +} + +extend Stream { + optional Eth2 eth2 = 121; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..9099dbb --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,62 @@ + + eth2 + + + + 0 + 0 + 166 + 72 + + + + Form + + + + + + Ethernet II + + + + + + Ethernet Type + + + leType + + + + + + + true + + + HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..625d3c7 --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,455 @@ +#include +#include + +#include "ip4.h" + +Ip4ConfigForm *Ip4Protocol::configForm = NULL; + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new Ip4ConfigForm; +} + +Ip4Protocol::~Ip4Protocol() +{ +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::ip4)->CopyFrom(data); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::ip4)) + data.MergeFrom(stream.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return (data.ver_hdrlen() >> 4) & 0x0F; + case FieldTextValue: + return QString("%1").arg((data.ver_hdrlen() >> 4) & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.ver_hdrlen() & 0xF0)); + case FieldBitSize: + return 4; + default: + break; + } + break; + case ip4_hdrLen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return data.ver_hdrlen() & 0x0F; + case FieldTextValue: + return QString("%1").arg(data.ver_hdrlen() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.ver_hdrlen() << 4)); + case FieldBitSize: + return 4; + default: + break; + } + break; + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + return data.totlen(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.totlen(), (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.totlen()); + default: + break; + } + break; + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + // FIXME need to shift for 13 bits + fv.resize(2); + qToBigEndian((quint16) data.frag_ofs(), (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + return data.proto(); + case FieldFrameValue: + return QByteArray(1, (char)data.proto()); + case FieldTextValue: + return QString("0x%1"). + arg(data.proto(), 2, BASE_HEX, QChar('0')); + default: + break; + } + break; + case ip4_cksum: + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + return data.cksum(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.cksum(), 4, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_srcAddr: + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return data.src_ip(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.src_ip(), (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(data.src_ip()).toString(); + default: + break; + } + break; + case ip4_dstAddr: + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return data.dst_ip(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.dst_ip(), (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(data.dst_ip()).toString(); + default: + break; + } + break; + + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + + +QWidget* Ip4Protocol::configWidget() +{ + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + configForm->leIpVersion->setText(QString().setNum(data.ver_hdrlen() >> 4)); + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + + configForm->leIpHdrLen->setText(QString().setNum(data.ver_hdrlen() & 0x0F)); + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), QString(), 1)); + + configForm->leIpLength->setText(QString().setNum(data.totlen())); + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + + configForm->leIpId->setText(uintToHexStr(data.id(), QString(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + configForm->leIpProto->setText(uintToHexStr(data.proto(), QString(), 1)); + + configForm->leIpCksum->setText(uintToHexStr(data.cksum(), QString(), 2)); + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..73d3004 --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,83 @@ +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_UNUSED 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_MF 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + static Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(Stream *parent = 0); + virtual ~Ip4Protocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..af61255 --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,47 @@ +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + // TODO: rename flags to frag_flags + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFFFF]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFFFF]; + + // TODO: Options +} + +extend Stream { + optional Ip4 ip4 = 130; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..35a85bf --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,479 @@ + + ip4 + + + + 0 + 0 + 504 + 296 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + 4 + + + + + + + Override Header Length + + + + + + + false + + + 5 + + + + + + + TOS/DSCP + + + + + + + HH; + + + + + + + + + + false + + + ... + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + HH HH; + + + + + + + + + Qt::Vertical + + + + + + + + + Fragment Offset + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + 64 + + + + + + + Protocol + + + + + + + false + + + + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 255.255.255.255 + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 255.255.255.255 + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 118 + 43 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 79 + 97 + + + 172 + 97 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..394ca2c --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,139 @@ +#include +#include + +#include "llc.h" + +LlcConfigForm *LlcProtocol::configForm = NULL; + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new LlcConfigForm; +} + +LlcProtocol::~LlcProtocol() +{ +} + +void LlcProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::llc)->CopyFrom(data); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::llc)) + data.MergeFrom(stream.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return data.dsap(); + case FieldTextValue: + return QString("%1").arg(data.dsap(), BASE_HEX); + case FieldFrameValue: + return QByteArray(1, (char)(data.dsap())); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return data.ssap(); + case FieldTextValue: + return QString("%1").arg(data.ssap(), BASE_HEX); + case FieldFrameValue: + return QByteArray(1, (char)(data.ssap())); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return data.ctl(); + case FieldTextValue: + return QString("%1").arg(data.ctl(), BASE_HEX); + case FieldFrameValue: + return QByteArray(1, (char)(data.ctl())); + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* LlcProtocol::configWidget() +{ + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ + configForm->leDsap->setText(QString("%1").arg(data.dsap(), 2, BASE_HEX, QChar('0'))); + configForm->leSsap->setText(QString("%1").arg(data.ssap(), 2, BASE_HEX, QChar('0'))); + configForm->leControl->setText(QString("%1").arg(data.ctl(), 2, BASE_HEX, QChar('0'))); +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); + data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); + data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..18e1181 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,55 @@ +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + static LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(Stream *parent = 0); + virtual ~LlcProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..c57cdbe --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,13 @@ +import "protocol.proto"; + +package OstProto; + +message Llc { + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Stream { + optional Llc llc = 123; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..4518b3d --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,108 @@ + + llc + + + + 0 + 0 + 304 + 72 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + leDsap + + + + + + + true + + + HH; + + + + + + + SSAP + + + leSsap + + + + + + + true + + + HH; + + + + + + + Control + + + leControl + + + + + + + true + + + HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..59b2607 --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,207 @@ +#include +#include + +#include "mac.h" + +MacConfigForm *MacProtocol::configForm = NULL; + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new MacConfigForm; +} + +MacProtocol::~MacProtocol() +{ +} + +void MacProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::mac)->CopyFrom(data); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::mac)) + data.MergeFrom(stream.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return data.dst_mac(); + case FieldTextValue: + return QString("%1").arg(data.dst_mac(), 12, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) data.dst_mac(), (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + + case mac_srcAddr: + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return data.src_mac(); + case FieldTextValue: + return QString("%1").arg(data.src_mac(), 12, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) data.src_mac(), (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* MacProtocol::configWidget() +{ + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), str, 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), QString(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..2842475 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,63 @@ +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + static MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(Stream *parent = 0); + virtual ~MacProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..c4c5253 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,29 @@ +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint32 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Stream { + optional Mac mac = 51; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..9095f29 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,190 @@ + + mac + + + + 0 + 0 + 423 + 144 + + + + Form + + + + + + MAC + + + + + + Mode + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + 1 + + + 1 + + + + + + + false + + + 1 + + + 1 + + + + + + + Source + + + + + + + HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + 1 + + + + + + + false + + + 1 + + + 1 + + + + + + + Count + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..3bd8e9c --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,63 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +LIBS += \ + -lprotobuf +FORMS += \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + ip4.ui \ + tcp.ui \ + udp.ui +PROTOS += \ + protocol.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + ip4.proto \ + tcp.proto \ + udp.proto +HEADERS += \ + abstractprotocol.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + ip4.h \ + tcp.h \ + udp.h +SOURCES += \ + abstractprotocol.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + ip4.cpp \ + tcp.cpp \ + udp.cpp + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..8feecf9 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,176 @@ +#include +#include + +#include "../client/stream.h" +#include "payload.h" + +PayloadConfigForm *PayloadProtocol::configForm = NULL; + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new PayloadConfigForm; +} + +PayloadProtocol::~PayloadProtocol() +{ +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::payload)->CopyFrom(data); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::payload)) + data.MergeFrom(stream.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + // FIXME: cannot use stream since it is only on client not + // on server + //dataLen = stream->frameLen() - stream->protocolHeaderSize(); + dataLen = 64; + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* PayloadProtocol::configWidget() +{ + return configForm; + //return new PayloadConfigForm; +} + +void PayloadProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), QString(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..aa66f5d --- /dev/null +++ b/common/payload.h @@ -0,0 +1,55 @@ +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + static PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(Stream *parent = 0); + virtual ~PayloadProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..e97f33c --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,22 @@ +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Stream { + optional Payload payload = 52; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..9de7ce1 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,69 @@ + + payload + + + + 0 + 0 + 142 + 98 + + + + Form + + + + + + Data Pattern + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + diff --git a/common/protocol.h b/common/protocol.h deleted file mode 100644 index 01f35f6..0000000 --- a/common/protocol.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef _PROTOCOL_H -#define _PROTOCOL_H - -#define UINT8 unsigned char -#define UINT16 unsigned short -#define UINT32 unsigned int - -#define BYTESWAP4(x) \ - (((x & 0xFF000000) >> 24) | \ - ((x & 0x00FF0000) >> 8) | \ - ((x & 0x0000FF00) << 8) | \ - ((x & 0x000000FF) << 24)) - -#define BYTESWAP2(x) \ - (((x & 0xFF00) >> 8) | \ - ((x & 0x00FF) << 8)) - -// TODO: portability -#define HTONL(x) BYTESWAP4(x) -#define NTOHL(x) BYTESWAP4(x) -#define HTONS(x) BYTESWAP2(x) -#define NTOHS(x) BYTESWAP2(x) - - -typedef struct { - UINT8 ver; - UINT8 resv1; - UINT16 resv2; - UINT16 msgType; - UINT16 msgLen; -} tCommHdr; - -typedef enum { - 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=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; -} tTlv; - -typedef struct { - UINT16 tlvType; - UINT16 tlvLen; - UINT32 portId; - UINT32 portSpeed; -#define TLV_MAX_PORT_NAME 64 -#define TLV_MAX_PORT_DESC 64 - 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/common/protocol.proto b/common/protocol.proto index 7038517..a809795 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -4,177 +4,11 @@ package OstProto; -// Ethernet -message Mac { - - enum MacAddrMode { - e_mm_fixed = 0; - e_mm_inc = 1; - e_mm_dec = 2; - } - - // Dst Mac - optional uint64 dst_mac = 1; - optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; - optional uint32 dst_mac_count = 3 [default = 16]; - optional uint32 dst_mac_step = 4 [default = 1]; - - // Src Mac - optional uint32 src_mac = 5; - optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; - optional uint32 src_mac_count = 7 [default = 16]; - optional uint32 src_mac_step = 8 [default = 1]; -} - -message Llc { - optional uint32 dsap = 1; - optional uint32 ssap = 2; - optional uint32 ctl = 3; -} - -message Snap { - optional uint32 oui = 1; - //optional uint32 type = 2; -} - -message Eth2 { - optional uint32 type = 1; -} - -message Vlan { - // VLAN presence/absence - optional bool is_cvlan_tagged = 9; - optional bool is_ctpid_override = 10; - optional bool is_svlan_tagged = 11; - optional bool is_stpid_override = 12; - - // VLAN values - optional uint32 ctpid = 13; - optional uint32 cvlan_tag = 14; // includes prio, cfi and cvlanid - optional uint32 stpid = 15; - optional uint32 svlan_tag = 16; // includes pcp, de and svlanid -} - -// IP -message Ip { - - enum IpAddrMode { - e_im_fixed = 0; - e_im_inc_host = 1; - e_im_dec_host = 2; - e_im_random_host = 3; - } - - optional bool is_override_ver = 1; - optional bool is_override_hdrlen = 2; - optional bool is_override_totlen = 3; - optional bool is_override_cksum = 4; - - optional uint32 ver_hdrlen = 5 [default = 0x45]; - optional uint32 tos = 6; - optional uint32 tot_len = 7; - optional uint32 id = 8 [default = 1234]; - // TODO: rename flags to frag_flags - optional uint32 flags = 9; - optional uint32 frag_ofs = 10; - optional uint32 ttl = 11 [default = 127]; - optional uint32 proto = 12; - optional uint32 cksum = 13; - - // Source IP - optional fixed32 src_ip = 14; - optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; - optional uint32 src_ip_count = 16 [default = 16]; - optional fixed32 src_ip_mask = 17 [default = 0xFFFFFFFF]; - - // Destination IP - optional fixed32 dst_ip = 18; - optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; - optional uint32 dst_ip_count = 20 [default = 16]; - optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFFFF]; - - // TODO: Options -} - -message Arp { -// TODO: ARP -} - -message Tcp { - - optional bool is_override_hdrlen = 1; - optional bool is_override_cksum = 2; - - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; - - optional uint32 seq_num = 5 [default = 129018]; - optional uint32 ack_num = 6; - - optional uint32 hdrlen_rsvd = 7 [default = 0x50]; - optional uint32 flags = 8; - - optional uint32 window = 9 [default = 1024]; - optional uint32 cksum = 10; - optional uint32 urg_ptr = 11; -} - -// UDP -message Udp { - optional bool is_override_totlen = 1; - optional bool is_override_cksum = 2; - - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; - optional uint32 totlen = 5; - optional uint32 cksum = 6; -} - -// TODO: ICMP -message Icmp { -} - -// TODO: IGMP -message Igmp { -} - message StreamId { required uint32 id = 1; } message StreamCore { - - enum FrameType { - e_ft_none = 0; - e_ft_eth_2 = 1; - e_ft_802_3_raw = 2; - e_ft_802_3_llc = 3; - e_ft_snap = 4; - } - - enum L3Proto { - e_l3_none = 0; - e_l3_ip = 1; - e_l3_arp = 2; - //e_l3_other = 3; - } - - enum L4Proto { - e_l4_none = 0; - e_l4_tcp = 1; - e_l4_udp = 2; - e_l4_icmp = 3; - e_l4_igmp = 4; - //e_l4_other = 5; - } - - enum DataPatternMode { - e_dp_fixed_word = 0; - e_dp_inc_byte = 1; - e_dp_dec_byte = 2; - e_dp_random = 3; - } - enum FrameLengthMode { e_fl_fixed = 0; e_fl_inc = 1; @@ -187,11 +21,6 @@ message StreamCore { optional bool is_enabled = 2; optional uint32 ordinal = 3; - // Data Pattern - optional DataPatternMode pattern_mode = 11; - optional uint32 pattern = 12; - optional uint32 data_start_ofs = 13; - // Frame Length (includes CRC) optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; optional uint32 frame_len = 15 [default = 64]; @@ -199,9 +28,7 @@ message StreamCore { optional uint32 frame_len_max = 17 [default = 1518]; // Currently Selected Protocols - optional FrameType ft = 21 [default = e_ft_none]; - optional L3Proto l3_proto = 22; - optional L4Proto l4_proto = 23; + repeated uint32 frame_proto = 20; } message StreamControl { @@ -240,24 +67,8 @@ message Stream { optional StreamCore core = 2; optional StreamControl control = 3; - // Protocol data - L2 - optional Mac mac = 51; - - optional Llc llc = 52; - optional Snap snap = 53; - optional Eth2 eth2 = 54; - optional Vlan vlan = 55; - - // Protocol data - L3 - optional Ip ip = 61; - optional Arp arp = 62; - - // Protocol data - L4 - optional Tcp tcp = 71; - optional Udp udp = 72; - optional Icmp icmp = 73; - optional Igmp igmp = 74; - + extensions 51 to 100; // Reserved for Ostinato Use + extensions 101 to 200; // Available for use by protocols } message Void { @@ -281,7 +92,6 @@ message StreamIdList { repeated StreamId stream_id = 2; } - message Port { required PortId port_id = 1; optional string name = 2; diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..33f841e --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,112 @@ +#include +#include + +#include "snap.h" + +SnapConfigForm *SnapProtocol::configForm = NULL; + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new SnapConfigForm; +} + +SnapProtocol::~SnapProtocol() +{ +} + +void SnapProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::snap)->CopyFrom(data); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::snap)) + data.MergeFrom(stream.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + return data.oui(); + case FieldTextValue: + return QString("%1").arg(data.oui(), 6, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.oui(), (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* SnapProtocol::configWidget() +{ + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + configForm->leOui->setText(uintToHexStr(data.oui(), str, 3)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_oui(configForm->leOui->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..9913b0f --- /dev/null +++ b/common/snap.h @@ -0,0 +1,50 @@ +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + static SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + + snap_fieldCount + }; + +public: + SnapProtocol(Stream *parent = 0); + virtual ~SnapProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..b6c310c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,12 @@ +import "protocol.proto"; + +package OstProto; + +message Snap { + optional uint32 oui = 1; + //optional uint32 type = 2; +} + +extend Stream { + optional Snap snap = 124; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..1f2b789 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,89 @@ + + snap + + + + 0 + 0 + 293 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + true + + + HH HH HH; + + + + + + + Type + + + + + + + true + + + HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..4c81c1c --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,360 @@ +#include +#include + +#include "tcp.h" + +TcpConfigForm *TcpProtocol::configForm = NULL; + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new TcpConfigForm; +} + +TcpProtocol::~TcpProtocol() +{ +} + +void TcpProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::tcp)->CopyFrom(data); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::tcp)) + data.MergeFrom(stream.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return data.src_port(); + case FieldTextValue: + return QString("%1").arg(data.src_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_dst_port: + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return data.dst_port(); + case FieldTextValue: + return QString("%1").arg(data.dst_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + case FieldTextValue: + return QString("%1").arg((data.hdrlen_rsvd() >> 4) & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return data.cksum(); + case FieldTextValue: + return QString("%1").arg(data.cksum()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* TcpProtocol::configWidget() +{ + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); + configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); + + configForm->leTcpSeqNum->setText(QString().setNum(data.seq_num())); + configForm->leTcpAckNum->setText(QString().setNum(data.ack_num())); + + configForm->leTcpHdrLen->setText(QString().setNum((data.hdrlen_rsvd() >> 4) & 0x0F)); + configForm->cbTcpHdrLenOverride->setChecked(data.is_override_hdrlen()); + + configForm->leTcpWindow->setText(QString().setNum(data.window())); + + configForm->leTcpCksum->setText(QString().setNum(data.cksum())); + configForm->cbTcpCksumOverride->setChecked(data.is_override_cksum()); + + configForm->leTcpUrgentPointer->setText(QString().setNum(data.urg_ptr())); + + configForm->cbTcpFlagsUrg->setChecked((data.flags() & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((data.flags() & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((data.flags() & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((data.flags() & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((data.flags() & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((data.flags() & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + bool isOk; + int ff = 0; + + data.set_src_port(configForm->leTcpSrcPort->text().toULong(&isOk)); + data.set_dst_port(configForm->leTcpDstPort->text().toULong(&isOk)); + + data.set_seq_num(configForm->leTcpSeqNum->text().toULong(&isOk)); + data.set_ack_num(configForm->leTcpAckNum->text().toULong(&isOk)); + + data.set_hdrlen_rsvd((configForm->leTcpHdrLen->text().toULong(&isOk) << 4) & 0xF0); + data.set_is_override_hdrlen(configForm->cbTcpHdrLenOverride->isChecked()); + + data.set_window(configForm->leTcpWindow->text().toULong(&isOk)); + + data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_is_override_cksum(configForm->cbTcpCksumOverride->isChecked()); + + data.set_urg_ptr(configForm->leTcpUrgentPointer->text().toULong(&isOk)); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + data.set_flags(ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..9fc2367 --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,69 @@ +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x01 +#define TCP_FLAG_ACK 0x02 +#define TCP_FLAG_PSH 0x04 +#define TCP_FLAG_RST 0x08 +#define TCP_FLAG_SYN 0x10 +#define TCP_FLAG_FIN 0x20 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + static TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(Stream *parent = 0); + virtual ~TcpProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..8144bad --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,27 @@ +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + + optional bool is_override_hdrlen = 1; + optional bool is_override_cksum = 2; + + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; + + optional uint32 seq_num = 5 [default = 129018]; + optional uint32 ack_num = 6; + + optional uint32 hdrlen_rsvd = 7 [default = 0x50]; + optional uint32 flags = 8; + + optional uint32 window = 9 [default = 1024]; + optional uint32 cksum = 10; + optional uint32 urg_ptr = 11; +} + +extend Stream { + optional Tcp tcp = 140; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..4a333be --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,228 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Source Port + + + + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + Destination Port + + + + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..c2e1bfe --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,200 @@ +#include +#include + +#include "udp.h" + +UdpConfigForm *UdpProtocol::configForm = NULL; + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(Stream *parent) + : AbstractProtocol(parent) +{ + if (configForm == NULL) + configForm = new UdpConfigForm; +} + +UdpProtocol::~UdpProtocol() +{ +} + +void UdpProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::udp)->CopyFrom(data); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::udp)) + data.MergeFrom(stream.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return data.src_port(); + case FieldTextValue: + return QString("%1").arg(data.src_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case udp_dstPort: + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return data.dst_port(); + case FieldTextValue: + return QString("%1").arg(data.dst_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case udp_totLen: + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + return data.totlen(); + case FieldTextValue: + return QString("%1").arg(data.totlen()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.totlen(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case udp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return data.cksum(); + case FieldTextValue: + return QString("%1").arg(data.cksum()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* UdpProtocol::configWidget() +{ + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, str, size) QString().setNum(num, 16) + configForm->leUdpSrcPort->setText(QString().setNum(data.src_port())); + configForm->leUdpDstPort->setText(QString().setNum(data.dst_port())); + + configForm->leUdpLength->setText(QString().setNum(data.totlen())); + configForm->cbUdpLengthOverride->setChecked(data.is_override_totlen()); + + configForm->leUdpCksum->setText(QString().setNum(data.cksum())); + configForm->cbUdpCksumOverride->setChecked(data.is_override_cksum()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_src_port(configForm->leUdpSrcPort->text().toULong(&isOk)); + data.set_dst_port(configForm->leUdpDstPort->text().toULong(&isOk)); + + data.set_totlen(configForm->leUdpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbUdpLengthOverride->isChecked()); + + data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_is_override_cksum(configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..4fe5147 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,56 @@ +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + static UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(Stream *parent = 0); + virtual ~UdpProtocol(); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..5f9680d --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,18 @@ +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_totlen = 1; + optional bool is_override_cksum = 2; + + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; + optional uint32 totlen = 5; + optional uint32 cksum = 6; +} + +extend Stream { + optional Udp udp = 141; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..e8e29d3 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,101 @@ + + udp + + + + 0 + 0 + 217 + 144 + + + + Form + + + + + + + + Source Port + + + + + + + + + + Destination Port + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..d5ac773 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,17 @@ +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_tpid_override = 10; + + // VLAN values + optional uint32 ctpid = 13; + optional uint32 cvlan_tag = 14; // includes prio, cfi and cvlanid + optional uint32 stpid = 15; + optional uint32 svlan_tag = 16; // includes pcp, de and svlanid +} + +extend Stream { + optional Vlan vlan = 126; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..0ae3be6 --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,168 @@ + + Form + + + + 0 + 0 + 271 + 90 + + + + Form + + + + + + + + Priority + + + + + + + CFI + + + + + + + VLAN + + + + + + + true + + + Override TPID + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + true + + + HH HH; + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro index 79c0e08..3465da3 100644 --- a/rpc/pbrpc.pro +++ b/rpc/pbrpc.pro @@ -1,13 +1,8 @@ TEMPLATE = lib -CONFIG += qt +CONFIG += qt staticlib QT += network DEFINES += HAVE_REMOTE INCLUDEPATH += "c:\msys\1.0\local\include" LIBS += -L"C:\msys\1.0\local\lib" -lprotobuf HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h SOURCES += rpcserver.cpp pbrpcchannel.cpp -client.path = ..\client\debug -client.files = debug\libpbrpc.a debug\pbrpc.dll -server.path = ..\server\debug -server.files = debug\libpbrpc.a debug\pbrpc.dll -INSTALLS += client server diff --git a/server/drone.pro b/server/drone.pro index 15d69b1..c42d9a4 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -3,15 +3,17 @@ CONFIG += qt debug QT += network DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "../rpc" +LIBS += -lprotobuf win32:LIBS += -lwpcap -lpacket unix:LIBS += -lpcap +win32:LIBS += -L"../common/debug" -lostproto +unix:LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc +POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" HEADERS += drone.h FORMS += drone.ui SOURCES += drone_main.cpp drone.cpp SOURCES += myservice.cpp - SOURCES += pcapextra.cpp -SOURCES += "..\common\protocol.pb.cc" diff --git a/server/myservice.cpp b/server/myservice.cpp index 085f6f5..20bbda0 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -4,6 +4,17 @@ #include #include +#include "../common/mac.h" +#include "../common/payload.h" + +#include "../common/eth2.h" // FIXME: proto DB +#include "../common/dot3.h" // FIXME: proto DB +#include "../common/llc.h" // FIXME: proto DB +#include "../common/snap.h" // FIXME: proto DB +#include "../common/ip4.h" // FIXME: proto DB +#include "../common/tcp.h" // FIXME: proto DB +#include "../common/udp.h" // FIXME: proto DB + #if 0 #include #include @@ -12,6 +23,64 @@ #define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} #define MB (1024*1024) +StreamInfo::StreamInfo() +{ + PbHelper pbh; + + pbh.ForceSetSingularDefault(&mCore); + pbh.ForceSetSingularDefault(&mControl); + + mProtocolList.append(new MacProtocol); + mProtocolList.append(new PayloadProtocol()); + + // FIXME: proto DB + mProtocolList.append(new Eth2Protocol); + mProtocolList.append(new Dot3Protocol); + mProtocolList.append(new LlcProtocol); + mProtocolList.append(new SnapProtocol); + mProtocolList.append(new Ip4Protocol); + mProtocolList.append(new TcpProtocol); + mProtocolList.append(new UdpProtocol); +} + +StreamInfo::~StreamInfo() +{ + for (int i = 0; i < mProtocolList.size(); i++) + delete mProtocolList.at(i); +} + +AbstractProtocol* StreamInfo::protocolById(int id) +{ + // FIXME BAD BAD VERY BAD! + switch(id) { + case 51: + return mProtocolList.at(0); + case 52: + return mProtocolList.at(1); + case 121: + return mProtocolList.at(2); + case 122: + return mProtocolList.at(3); + case 123: + return mProtocolList.at(4); + case 124: + return mProtocolList.at(5); + // case 125 (unused) +#if 0 // todo VLAN + case 126: + return mProtocolList.at(x); +#endif + case 130: + return mProtocolList.at(6); + case 140: + return mProtocolList.at(7); + case 141: + return mProtocolList.at(8); + default: + return NULL; + } +} + quint32 StreamInfo::pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, quint8 protocol, quint16 len) { @@ -76,33 +145,29 @@ quint16 StreamInfo::ipv4Cksum(uchar *buf, int len, quint32 partialSum) int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) { - int u, pktLen, dataLen, len = 0; - quint32 srcIp, dstIp; // need it later for TCP/UDP cksum calculation - quint32 cumCksum = 0; // cumulative cksum used to combine partial cksums - int tcpOfs, udpOfs; // needed to fill in cksum later - uchar scratch[8]; + int pktLen, len = 0; // Decide a frame length based on length mode - switch(d.core().len_mode()) + switch(mCore.len_mode()) { case OstProto::StreamCore::e_fl_fixed: - pktLen = d.core().frame_len(); + pktLen = mCore.frame_len(); break; case OstProto::StreamCore::e_fl_inc: - pktLen = d.core().frame_len_min() + (n % - (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + pktLen = mCore.frame_len_min() + (n % + (mCore.frame_len_max() - mCore.frame_len_min() + 1)); break; case OstProto::StreamCore::e_fl_dec: - pktLen = d.core().frame_len_max() - (n % - (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + pktLen = mCore.frame_len_max() - (n % + (mCore.frame_len_max() - mCore.frame_len_min() + 1)); break; case OstProto::StreamCore::e_fl_random: - pktLen = d.core().frame_len_min() + (qrand() % - (d.core().frame_len_max() - d.core().frame_len_min() + 1)); + pktLen = mCore.frame_len_min() + (qrand() % + (mCore.frame_len_max() - mCore.frame_len_min() + 1)); break; default: qWarning("Unhandled len mode %d. Using default 64", - d.core().len_mode()); + mCore.len_mode()); pktLen = 64; break; } @@ -113,6 +178,31 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) if ((pktLen < 0) || (pktLen > bufMaxSize)) return 0; + // FIXME: Calculated pktLen is an input to Payload Protocol + + // FIXME: checksums!!! + + for (int i = 0; i < mCore.frame_proto_size(); i++) + { + QByteArray ba; + + ba = protocolById(mCore.frame_proto(i))->protocolFrameValue(n); + if (len + ba.size() < bufMaxSize) + { + memcpy(buf+len, ba.constData(), ba.size()); + } + len += ba.size(); + } + + return pktLen; + +#if 0 // Proto FW + int u, pktLen, dataLen, len = 0; + quint32 srcIp, dstIp; // need it later for TCP/UDP cksum calculation + quint32 cumCksum = 0; // cumulative cksum used to combine partial cksums + int tcpOfs, udpOfs; // needed to fill in cksum later + uchar scratch[8]; + // We always have a Mac Header! switch (d.mac().dst_mac_mode()) { @@ -449,7 +539,9 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) default: qWarning("Unhandled data pattern %d", d.core().pattern_mode()); } +#endif +#if 0 // Proto FW // Calculate TCP/UDP checksum over the data pattern/payload and fill in switch (d.core().l4_proto()) { @@ -473,8 +565,9 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) // No cksum processing required break; } - return pktLen; +#endif + } @@ -597,28 +690,28 @@ void PortInfo::update() for (int i = 0; i < streamList.size(); i++) { //_restart: - if (streamList[i].d.core().is_enabled()) + if (streamList[i]->mCore.is_enabled()) { long numPackets, numBursts; long ibg, ipg; - switch (streamList[i].d.control().unit()) + switch (streamList[i]->mControl.unit()) { case OstProto::StreamControl::e_su_bursts: - numBursts = streamList[i].d.control().num_bursts(); - numPackets = streamList[i].d.control().packets_per_burst(); - ibg = 1000000/streamList[i].d.control().bursts_per_sec(); + numBursts = streamList[i]->mControl.num_bursts(); + numPackets = streamList[i]->mControl.packets_per_burst(); + ibg = 1000000/streamList[i]->mControl.bursts_per_sec(); ipg = 0; break; case OstProto::StreamControl::e_su_packets: numBursts = 1; - numPackets = streamList[i].d.control().num_packets(); + numPackets = streamList[i]->mControl.num_packets(); ibg = 0; - ipg = 1000000/streamList[i].d.control().packets_per_sec(); + ipg = 1000000/streamList[i]->mControl.packets_per_sec(); break; default: qWarning("Unhandled stream control unit %d", - streamList[i].d.control().unit()); + streamList[i]->mControl.unit()); continue; } qDebug("numBursts = %ld, numPackets = %ld\n", @@ -631,7 +724,7 @@ void PortInfo::update() { int len; - len = streamList[i].makePacket(pktBuf, sizeof(pktBuf), + len = streamList[i]->makePacket(pktBuf, sizeof(pktBuf), j * numPackets + k); if (len > 0) { @@ -680,7 +773,7 @@ void PortInfo::update() } } // for (numBursts) - switch(streamList[i].d.control().next()) + switch(streamList[i]->mControl.next()) { case ::OstProto::StreamControl::e_nw_stop: goto _stop_no_more_pkts; @@ -705,7 +798,7 @@ void PortInfo::update() default: qFatal("---------- %s: Unhandled case (%d) -----------", - __FUNCTION__, streamList[i].d.control().next() ); + __FUNCTION__, streamList[i]->mControl.next() ); break; } @@ -1124,7 +1217,7 @@ int MyService::getStreamIndex(unsigned int portIdx, for (i = 0; i < portInfo[portIdx]->streamList.size(); i++) { - if (streamId == portInfo[portIdx]->streamList.at(i).d.stream_id().id()) + if (streamId == portInfo[portIdx]->streamList.at(i)->mStreamId.id()) goto _found; } @@ -1247,12 +1340,10 @@ const ::OstProto::PortId* request, response->mutable_port_id()->set_id(portIdx); for (int j = 0; j < portInfo[portIdx]->streamList.size(); j++) { - OstProto::StreamId *s, *q; - - q = portInfo[portIdx]->streamList[j].d.mutable_stream_id(); + OstProto::StreamId *s; s = response->add_stream_id(); - s->CopyFrom(*q); + s->CopyFrom(portInfo[portIdx]->streamList[j]->mStreamId); } _exit: @@ -1286,7 +1377,19 @@ const ::OstProto::StreamIdList* request, continue; // TODO(LOW): Partial status of RPC s = response->add_stream(); - s->CopyFrom(portInfo[portIdx]->streamList[streamIndex].d); + + s->mutable_stream_id()->CopyFrom( + portInfo[portIdx]->streamList[streamIndex]->mStreamId); + s->mutable_core()->CopyFrom( + portInfo[portIdx]->streamList[streamIndex]->mCore); + s->mutable_control()->CopyFrom( + portInfo[portIdx]->streamList[streamIndex]->mControl); + for (int j=0; j < portInfo[portIdx]->streamList[streamIndex]-> + mProtocolList.size(); j++) + { + portInfo[portIdx]->streamList[streamIndex]-> + mProtocolList[j]->protoDataCopyInto(*s); + } } _exit: @@ -1312,7 +1415,7 @@ const ::OstProto::StreamIdList* request, for (int i = 0; i < request->stream_id_size(); i++) { int streamIndex; - StreamInfo s; + StreamInfo *s = new StreamInfo; // If stream with same id as in request exists already ==> error!! streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); @@ -1322,7 +1425,7 @@ const ::OstProto::StreamIdList* request, // Append a new "default" stream - actual contents of the new stream is // expected in a subsequent "modifyStream" request - set the stream id // now itself however!!! - s.d.mutable_stream_id()->CopyFrom(request->stream_id(i)); + s->mStreamId.CopyFrom(request->stream_id(i)); portInfo[portIdx]->streamList.append(s); // TODO(LOW): fill-in response "Ack"???? @@ -1357,7 +1460,7 @@ const ::OstProto::StreamIdList* request, if (streamIndex < 0) continue; // TODO(LOW): Partial status of RPC - portInfo[portIdx]->streamList.removeAt(streamIndex); + delete portInfo[portIdx]->streamList.takeAt(streamIndex); // TODO(LOW): fill-in response "Ack"???? } @@ -1391,8 +1494,17 @@ const ::OstProto::StreamConfigList* request, if (streamIndex < 0) continue; // TODO(LOW): Partial status of RPC - portInfo[portIdx]->streamList[streamIndex].d.MergeFrom( - request->stream(i)); + portInfo[portIdx]->streamList[streamIndex]->mCore.clear_frame_proto(); + portInfo[portIdx]->streamList[streamIndex]->mCore.MergeFrom( + request->stream(i).core()); + portInfo[portIdx]->streamList[streamIndex]->mControl.MergeFrom( + request->stream(i).control()); + for (int j=0; j < portInfo[portIdx]->streamList[streamIndex]-> + mProtocolList.size(); j++) + { + portInfo[portIdx]->streamList[streamIndex]-> + mProtocolList[j]->protoDataCopyFrom(request->stream(i)); + } // TODO(LOW): fill-in response "Ack"???? } diff --git a/server/myservice.h b/server/myservice.h index bbf019c..6a662fd 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -8,6 +8,7 @@ #endif #include "../common/protocol.pb.h" +#include "../common/abstractprotocol.h" #include "abstracthost.h" #include #include @@ -34,10 +35,18 @@ class StreamInfo friend class MyService; friend class PortInfo; - OstProto::Stream d; - - StreamInfo() { PbHelper pbh; pbh.ForceSetSingularDefault(&d); } + OstProto::StreamId mStreamId; + OstProto::StreamCore mCore; + OstProto::StreamControl mControl; + QList mProtocolList; +public: + StreamInfo(); + ~StreamInfo(); + +private: + AbstractProtocol* protocolById(int id); + quint32 pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, quint8 protocol, quint16 len); quint32 ipv4CksumPartial(uchar *buf, int len); @@ -45,7 +54,7 @@ class StreamInfo int makePacket(uchar *buf, int bufMaxSize, int n); public: bool operator < (const StreamInfo &s) const - { return(d.core().ordinal() < s.d.core().ordinal()); } + { return(mCore.ordinal() < s.mCore.ordinal()); } }; @@ -146,7 +155,7 @@ class PortInfo struct timeval lastTsTx; //! used for Rate Stats calculations /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ - QList streamList; + QList streamList; public: PortInfo(uint id, pcap_if_t *dev); From 2ec7fb30c2b19956871edd2138a8b7124b9bb02e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 10 May 2009 06:27:17 +0000 Subject: [PATCH 021/294] Protocol Framework basic code in place now. Cleanup pending. - New Classes: o ProtocolManager - singleton with which all protocols register o ProtocolCollection - Aggregates all registered protocols; exports methods to work on all protocols o StreamBase - aggregates ProtocolCollection with Stream Core and Control; the client/server side stream classes now derive from StreamBase leading to major reduction in their code (more cleanup pending) - AbstractProtocol now supports the additional methods o createInstance() o protocolFrameSize() o protocolFrameOffset(), protocolFramePayloadSize() o protocolId(), payloadProtocolId() o protocolFrameCksum(), protocolFramePayloadCksum() 0 constructor takes an extra param - frameProtoList - Specific protocols - eth2, llc, snap, ip4, udp, tcp now return length, protocol id and cksums correctly (tcp/udp cksum pending) - StreamConfigDialog - protocol controls for length, cksum and protocolid are automatically updated (not fully working yet) --- Makefile | 6 + client/port.cpp | 4 +- client/stream.cpp | 162 +---------------- client/stream.h | 76 +------- client/streamconfigdialog.cpp | 328 ++++++---------------------------- client/streamconfigdialog.h | 18 +- client/streamconfigdialog.ui | 200 +++++---------------- common/abstractprotocol.cpp | 148 ++++++++++++++- common/abstractprotocol.h | 36 +++- common/dot3.cpp | 37 +++- common/dot3.h | 7 +- common/eth2.cpp | 34 ++-- common/eth2.h | 7 +- common/eth2.ui | 4 +- common/ip4.cpp | 124 +++++++++++-- common/ip4.h | 9 +- common/ip4.ui | 22 ++- common/llc.cpp | 56 ++++-- common/llc.h | 7 +- common/llc.ui | 12 +- common/mac.cpp | 18 +- common/mac.h | 7 +- common/mac.ui | 4 +- common/ostproto.pro | 6 + common/payload.cpp | 27 +-- common/payload.h | 7 +- common/payload.ui | 2 +- common/protocolcollection.cpp | 106 +++++++++++ common/protocolcollection.h | 30 ++++ common/protocolmanager.cpp | 38 ++++ common/protocolmanager.h | 18 ++ common/snap.cpp | 59 +++++- common/snap.h | 9 +- common/snap.proto | 2 +- common/snap.ui | 8 +- common/streambase.cpp | 69 +++++++ common/streambase.h | 36 ++++ common/tcp.cpp | 27 ++- common/tcp.h | 8 +- common/tcp.ui | 2 +- common/udp.cpp | 56 +++++- common/udp.h | 8 +- common/udp.ui | 37 +++- common/vlan.ui | 2 +- server/myservice.cpp | 181 ++++--------------- server/myservice.h | 16 +- 46 files changed, 1127 insertions(+), 953 deletions(-) create mode 100644 common/protocolcollection.cpp create mode 100644 common/protocolcollection.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h diff --git a/Makefile b/Makefile index c285580..574c0a1 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,12 @@ clean: $(MAKE) -C server $@ $(MAKE) -C client $@ +distclean: + $(MAKE) -C rpc $@ + $(MAKE) -C common $@ + $(MAKE) -C server $@ + $(MAKE) -C client $@ + qmake: cd rpc && qmake && cd .. cd common && qmake && cd .. diff --git a/client/port.cpp b/client/port.cpp index 64c7865..291b1b5 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -98,7 +98,7 @@ bool Port::updateStream(uint streamId, OstProto::Stream *stream) _found: streamIndex = i; - mStreams[streamIndex]->update(stream); + mStreams[streamIndex]->protoDataCopyFrom(*stream); reorderStreamsByOrdinals(); return true; @@ -167,7 +167,7 @@ void Port::getModifiedStreamsSinceLastSync( OstProto::Stream *s; s = streamConfigList.add_stream(); - mStreams[i]->getConfig(mPortId, *s); + mStreams[i]->protoDataCopyInto(*s); } qDebug("Done %s", __FUNCTION__); } diff --git a/client/stream.cpp b/client/stream.cpp index 3af3509..b978913 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -3,173 +3,27 @@ #include "stream.h" -#include "../common/mac.h" -#include "../common/payload.h" - -#include "../common/eth2.h" // FIXME: proto DB -#include "../common/dot3.h" // FIXME: proto DB -#include "../common/llc.h" // FIXME: proto DB -#include "../common/snap.h" // FIXME: proto DB -#include "../common/ip4.h" // FIXME: proto DB -#include "../common/tcp.h" // FIXME: proto DB -#include "../common/udp.h" // FIXME: proto DB - - -//----------------------------------------------------- -// Stream Class Methods -//----------------------------------------------------- Stream::Stream() { - mId = 0xFFFFFFFF; - - mCore = new OstProto::StreamCore; - mControl = new OstProto::StreamControl; - -// mCore->set_port_id(0xFFFFFFFF); -// mCore->set_stream_id(mId); - - mProtocolList.append(new MacProtocol); - mProtocolList.append(new PayloadProtocol(this)); - - // FIXME: proto DB - mProtocolList.append(new Eth2Protocol); - mProtocolList.append(new Dot3Protocol); - mProtocolList.append(new LlcProtocol); - mProtocolList.append(new SnapProtocol); - mProtocolList.append(new Ip4Protocol); - mProtocolList.append(new TcpProtocol); - mProtocolList.append(new UdpProtocol); - + //mId = 0xFFFFFFFF; mCore->set_is_enabled(true); - mCore->add_frame_proto(51); // MAC (FIXME: hardcoding) - mCore->add_frame_proto(52); // Payload (FIXME: hardcoding) + + QList protoList; + protoList.append(51); + protoList.append(52); + setFrameProtocol(protoList); } Stream::~Stream() { - for (int i = 0; i < mProtocolList.size(); i++) - delete mProtocolList.at(i); - - delete mControl; - delete mCore; -} - -void Stream::protoDataCopyFrom(Stream& stream) -{ - OstProto::Stream data; - - stream.getConfig(0, data); - update(&data); } void Stream::loadProtocolWidgets() { - for (int i=0; i < mProtocolList.size(); i++) - mProtocolList[i]->loadConfigWidget(); + protocols.loadConfigWidgets(); } void Stream::storeProtocolWidgets() { - for (int i=0; i < mProtocolList.size(); i++) - mProtocolList[i]->storeConfigWidget(); -} - -/*! Copy current client side config into the OstProto::Stream */ -// FIXME - remove portId unused param! -void Stream::getConfig(uint portId, OstProto::Stream &s) -{ - s.mutable_stream_id()->set_id(mId); - - s.mutable_core()->CopyFrom(*mCore); - s.mutable_control()->CopyFrom(*mControl); - - // FIXME - this doesn't take care of multiple headers of same proto - // e.g. IPinIP or double VLAN Tagged - // FIXME: change s from pointer to reference? - for (int i = 0; i < mProtocolList.size(); i++) - { - qDebug("%s: protocol %d", __FUNCTION__, i); - mProtocolList[i]->protoDataCopyInto(s); - } - - qDebug("%s: Done", __FUNCTION__); -} - -bool Stream::update(OstProto::Stream *stream) -{ - mCore->clear_frame_proto(); - mCore->MergeFrom(stream->core()); - mControl->MergeFrom(stream->control()); - - // FIXME - this doesn't take care of multiple headers of same proto - // e.g. IPinIP or double VLAN Tagged - // FIXME: change s from pointer to reference? - for (int i = 0; i < mProtocolList.size(); i++) - { - mProtocolList[i]->protoDataCopyFrom(*stream); - } - - // FIXME(MED): Re-eval why not store complete OstProto::Stream - // instead of components - return true; -} - -QList Stream::frameProtocol() -{ - QList protocolList; - - for (int i = 0; i < mCore->frame_proto_size(); i++) - protocolList.append(mCore->frame_proto(i)); - - return protocolList; -} - -void Stream::setFrameProtocol(QList protocolList) -{ - mCore->clear_frame_proto(); - for (int i = 0; i < protocolList.size(); i++) - mCore->add_frame_proto(protocolList.at(i)); -} - -int Stream::protocolHeaderSize() -{ - int size = 0; - - for (int i = 0; i < mCore->frame_proto_size(); i++) - size += protocolById(mCore->frame_proto(i))-> - protocolFrameValue().size(); - - return size; -} - -AbstractProtocol* Stream::protocolById(int id) -{ - // FIXME BAD BAD VERY BAD! - switch(id) { - case 51: - return mProtocolList.at(0); - case 52: - return mProtocolList.at(1); - case 121: - return mProtocolList.at(2); - case 122: - return mProtocolList.at(3); - case 123: - return mProtocolList.at(4); - case 124: - return mProtocolList.at(5); - // case 125 (unused) -#if 0 // todo VLAN - case 126: - return mProtocolList.at(x); -#endif - case 130: - return mProtocolList.at(6); - case 140: - return mProtocolList.at(7); - case 141: - return mProtocolList.at(8); - default: - return NULL; - } + protocols.storeConfigWidgets(); } diff --git a/client/stream.h b/client/stream.h index c4884d4..1c19b19 100644 --- a/client/stream.h +++ b/client/stream.h @@ -2,48 +2,21 @@ #define _STREAM_H #include - #include #include + #include "../common/protocol.pb.h" -#include "../common/abstractprotocol.h" +#include "../common/streambase.h" -// Convenience Defines FIXME -#define IP_PROTO_ICMP 0x01 -#define IP_PROTO_IGMP 0x02 -#define IP_PROTO_TCP 0x06 -#define IP_PROTO_UDP 0x11 +class Stream : public StreamBase { -class Stream { - - quint32 mId; - OstProto::StreamCore *mCore; - OstProto::StreamControl *mControl; - - QList mProtocolList; + //quint32 mId; public: - - void* core() { return mCore; } // FIXME(HI): Debug ONLY void loadProtocolWidgets(); void storeProtocolWidgets(); public: - enum FrameType { - e_ft_none, - e_ft_eth_2, - e_ft_802_3_raw, - e_ft_802_3_llc, - e_ft_snap - }; - - enum DataPatternMode { - e_dp_fixed_word, - e_dp_inc_byte, - e_dp_dec_byte, - e_dp_random - }; - enum FrameLengthMode { e_fl_fixed, e_fl_inc, @@ -51,20 +24,6 @@ public: e_fl_random }; - enum L3Proto { - e_l3_none, - e_l3_ip, - e_l3_arp, - }; - - enum L4Proto { - e_l4_none, - e_l4_tcp, - e_l4_udp, - e_l4_icmp, - e_l4_igmp, - }; - enum SendUnit { e_su_packets, e_su_bursts @@ -87,19 +46,15 @@ public: Stream(); ~Stream(); - void protoDataCopyFrom(Stream& stream); + // TODO: Below methods move to StreamBase??? bool operator < (const Stream &s) const { return(mCore->ordinal() < s.mCore->ordinal()); } - - void getConfig(uint portId, OstProto::Stream &s); - bool update(OstProto::Stream *stream); - quint32 id() - { return mId;} + { return mStreamId->id();} bool setId(quint32 id) - { mId = id; return true;} + { mStreamId->set_id(id); return true;} #if 0 // FIXME(HI): needed? quint32 portId() @@ -123,12 +78,7 @@ public: bool setName(QString name) { mCore->set_name(name.toStdString()); return true; } -// TODO(HI) : ????? -#if 0 - quint16 dataStartOfs; -#endif - - // Frame Length (includes CRC) + // Frame Length (includes FCS) FrameLengthMode lenMode() { return (FrameLengthMode) mCore->len_mode(); } bool setLenMode(FrameLengthMode lenMode) @@ -192,16 +142,6 @@ public: { return (quint32) mControl->bursts_per_sec(); } bool setBurstRate(quint32 burstsPerSec) { mControl->set_bursts_per_sec(burstsPerSec); return true; } - - //--------------------------------------------------------------- - // Methods for use by Packet Model - //--------------------------------------------------------------- - QList frameProtocol(); - void setFrameProtocol(QList protocolList); - - //! Includes ALL protocol headers excluding payload data - int protocolHeaderSize(); - AbstractProtocol* protocolById(int id); }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 0079240..b69ef67 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -7,71 +7,38 @@ int StreamConfigDialog::lastTopLevelTabIndex = 0; int StreamConfigDialog::lastProtoTabIndex = 0; -// TODO(HI): Write HexLineEdit::setNum() and num() and use it in -// Load/Store stream methods - StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) { + OstProto::Stream s; mCurrentStreamIndex = streamIndex; - mpStream = new Stream; - mpStream->protoDataCopyFrom(*(mPort.streamByIndex(mCurrentStreamIndex))); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); setupUi(this); setupUiExtra(); - // setupUi + connect(bgFrameType, SIGNAL(buttonClicked(int)), + this, SLOT(updateSelectedProtocols())); + connect(bgL3Proto, SIGNAL(buttonClicked(int)), + this, SLOT(updateSelectedProtocols())); + connect(bgL4Proto, SIGNAL(buttonClicked(int)), + this, SLOT(updateSelectedProtocols())); // Time to play match the signals and slots! connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); - // Show/Hide FrameType related inputs for FT None - connect(rbFtNone, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); - // Enable/Disable L3 Protocol Choices for FT None connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); - // Show/Hide FrameType related inputs for FT Ethernet2 - connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); - // Enable/Disable L3 Protocol Choices for FT Ethernet2 connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); - // Show/Hide FrameType related inputs for FT 802.3 Raw - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblDsap, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leDsap, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblSsap, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leSsap, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblControl, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leControl, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); - // Force L3 = None if FT = 802.3 Raw connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); @@ -80,18 +47,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); - // Show/Hide FrameType related inputs for FT 802.3 LLC - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblSsap, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leSsap, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblControl, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leControl, SLOT(setVisible(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblOui, SLOT(setHidden(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leOui, SLOT(setHidden(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), lblType, SLOT(setHidden(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), leType, SLOT(setHidden(bool))); - // Force L3 = None if FT = 802.3 LLC (to ensure a valid L3 is selected) connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); @@ -100,31 +55,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); - // Show/Hide FrameType related inputs for FT 802.3 LLC SNAP - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblSsap, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leSsap, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblControl, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leControl, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblOui, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leOui, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblType, SLOT(setVisible(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leType, SLOT(setVisible(bool))); - // Enable/Disable L3 Protocol Choices for FT 802.3 LLC SNAP connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); - // Enable/Disable FrameType related inputs for FT 802.3 LLC SNAP - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblDsap, SLOT(setDisabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leDsap, SLOT(setDisabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblSsap, SLOT(setDisabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leSsap, SLOT(setDisabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), lblControl, SLOT(setDisabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), leControl, SLOT(setDisabled(bool))); - // Enable/Disable L4 Protocol Choices for L3 Protocol None connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); connect(rbL3None, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); @@ -152,18 +87,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Force L4 Protocol = None if L3 Protocol is set to ARP connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); - //TODO: remove if not needed -#if 0 - // This set of 'clicks' is a hack to trigger signals at dialog creation - // time so that a coherent 'set' is initialized - // Actual stream values will be initialized by LoadCurrentStream() - rbL3Ipv4->click(); - rbL3None->click(); - rbFtEthernet2->click(); - rbFtNone->click(); -#endif - - //mmpStreamList = streamList; LoadCurrentStream(); mpPacketModel = new PacketModel(QList(), this); tvPacketTree->setModel(mpPacketModel); @@ -173,6 +96,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); // TODO(MED): + //! \todo Implement then enable these protocols + rbL3Arp->setHidden(true); + rbL4Icmp->setHidden(true); + rbL4Igmp->setHidden(true); //! \todo Enable navigation of streams pbPrev->setDisabled(true); pbNext->setDisabled(true); @@ -181,6 +108,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); + // Finally, restore the saved last selected tab for the various tab widgets twTopLevel->setCurrentIndex(lastTopLevelTabIndex); if (twProto->isTabEnabled(lastProtoTabIndex)) @@ -199,27 +127,25 @@ void StreamConfigDialog::setupUiExtra() layout = static_cast(twTopLevel->widget(0)->layout()); - layout->addWidget(mpStream->protocolById(52)->configWidget(), 0, 1); - qDebug("setupUi wgt = %p", mpStream->protocolById(52)->configWidget()); + layout->addWidget(mpStream->protocol(52)->configWidget(), 0, 1); + qDebug("setupUi wgt = %p", mpStream->protocol(52)->configWidget()); } // ---- Setup default stuff that cannot be done in designer ---- + bgFrameType = new QButtonGroup(); + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgFrameType->addButton(btn); - // Since the dialog defaults are FT = None, L3 = None, L4 = None; - // hide associated input fields since it can't be done in Designer - lblDsap->setHidden(true); - leDsap->setHidden(true); - lblSsap->setHidden(true); - leSsap->setHidden(true); - lblControl->setHidden(true); - leControl->setHidden(true); - lblOui->setHidden(true); - leOui->setHidden(true); - lblType->setHidden(true); - leType->setHidden(true); + bgL3Proto = new QButtonGroup(); + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgL3Proto->addButton(btn); - twProto->setTabEnabled(2, FALSE); - twProto->setTabEnabled(3, FALSE); + bgL4Proto = new QButtonGroup(); + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgL4Proto->addButton(btn); + + //twProto->setTabEnabled(2, FALSE); + //twProto->setTabEnabled(3, FALSE); /* ** Setup Validators @@ -263,18 +189,8 @@ StreamConfigDialog::~StreamConfigDialog() QLayout *layout = twTopLevel->widget(0)->layout(); if (layout) { - qDebug("dstrct wgt = %p", mpStream->protocolById(52)->configWidget()); -#if 0 - int i = layout->indexOf(mpStream->protocolById(52)->configWidget()); - if (i >= 0) - { - layout->takeAt(i); - mpStream->protocolById(52)->configWidget()->setParent(0); - } -#endif - layout->removeWidget(mpStream->protocolById(52)->configWidget()); - mpStream->protocolById(52)->configWidget()->setParent(0); - + layout->removeWidget(mpStream->protocol(52)->configWidget()); + mpStream->protocol(52)->configWidget()->setParent(0); } } @@ -296,6 +212,10 @@ StreamConfigDialog::~StreamConfigDialog() } } + delete bgFrameType; + delete bgL3Proto; + delete bgL4Proto; + delete mpStream; } @@ -340,6 +260,10 @@ void StreamConfigDialog::updateSelectedProtocols() // Payload mSelectedProtocols.append(52); + + mpStream->setFrameProtocol(mSelectedProtocols); + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); } @@ -399,146 +323,6 @@ void StreamConfigDialog::on_pbNext_clicked() #endif } -void StreamConfigDialog::on_rbFtNone_toggled(bool checked) -{ -} - -void StreamConfigDialog::on_rbFtEthernet2_toggled(bool checked) -{ -} - -void StreamConfigDialog::on_rbFt802Dot3Raw_toggled(bool checked) -{ -} - -void StreamConfigDialog::on_rbFt802Dot3Llc_toggled(bool checked) -{ -} - -void StreamConfigDialog::on_rbFtLlcSnap_toggled(bool checked) -{ - if (checked) - { - leDsap->setText("AA"); - leSsap->setText("AA"); - leControl->setText("03"); - } -} - -void StreamConfigDialog::on_rbL3Ipv4_toggled(bool checked) -{ - if (checked) - { - twProto->setTabEnabled(2, TRUE); - twProto->setTabText(2, "L3 (IPv4)"); - leType->setText("08 00"); - - // FIXME: Hardcoding - mpStream->protocolById(121)->setFieldData(0 /* type */, 0x800); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(2, FALSE); - twProto->setTabText(2, "L3"); - } -} - -void StreamConfigDialog::on_rbL3Arp_toggled(bool checked) -{ - if (checked) - { - twProto->setTabEnabled(2, TRUE); - twProto->setTabText(2, "L3 (ARP)"); - leType->setText("08 06"); - - // FIXME: Hardcoding - mpStream->protocolById(121)->setFieldData(0 /* type */, 0x806); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(2, FALSE); - twProto->setTabText(2, "L3"); - } -} - -void StreamConfigDialog::on_rbL4Icmp_toggled(bool checked) -{ - QString str; - - if (checked) - { - twProto->setTabEnabled(3, TRUE); - twProto->setTabText(3, "L4 (ICMP)"); - // FIXME: Hardcoding - mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_ICMP); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(3, FALSE); - twProto->setTabText(3, "L4"); - } -} - -void StreamConfigDialog::on_rbL4Igmp_toggled(bool checked) -{ - QString str; - - if (checked) - { - twProto->setTabEnabled(3, TRUE); - twProto->setTabText(3, "L4 (IGMP)"); - // FIXME: Hardcoding - mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_IGMP); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(3, FALSE); - twProto->setTabText(3, "L4"); - } -} - -void StreamConfigDialog::on_rbL4Tcp_toggled(bool checked) -{ - QString str; - - if (checked) - { - twProto->setTabEnabled(3, TRUE); - twProto->setTabText(3, "L4 (TCP)"); - // FIXME: Hardcoding - mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_TCP); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(3, FALSE); - twProto->setTabText(3, "L4"); - } -} - -void StreamConfigDialog::on_rbL4Udp_toggled(bool checked) -{ - QString str; - - if (checked) - { - twProto->setTabEnabled(3, TRUE); - twProto->setTabText(3, "L4 (UDP)"); - // FIXME: Hardcoding - mpStream->protocolById(130)->setFieldData(8 /* proto */, IP_PROTO_UDP); - mpStream->loadProtocolWidgets(); // FIXME: pass protocol as param - } - else - { - twProto->setTabEnabled(3, FALSE); - twProto->setTabText(3, "L4"); - } -} - void StreamConfigDialog::on_twTopLevel_currentChanged(int index) { QList protoList; @@ -549,8 +333,8 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) updateSelectedProtocols(); foreach(int i, mSelectedProtocols) - if (mpStream->protocolById(i)) - protoList.append(mpStream->protocolById(i)); + if (mpStream->protocol(i)) + protoList.append(mpStream->protocol(i)); mpPacketModel->setSelectedProtocols(protoList); StoreCurrentStream(mpStream); @@ -583,42 +367,44 @@ void StreamConfigDialog::on_twProto_currentChanged(int index) switch(index) { case 1: // L2 - wl.append(mpStream->protocolById(51)->configWidget()); + wl.append(mpStream->protocol("mac")->configWidget()); if (rbFtEthernet2->isChecked()) - wl.append(mpStream->protocolById(121)->configWidget()); + wl.append(mpStream->protocol("eth2")->configWidget()); else if (rbFt802Dot3Raw->isChecked()) - wl.append(mpStream->protocolById(122)->configWidget()); + wl.append(mpStream->protocol("dot3")->configWidget()); else if (rbFt802Dot3Llc->isChecked()) { - wl.append(mpStream->protocolById(122)->configWidget()); - wl.append(mpStream->protocolById(123)->configWidget()); + wl.append(mpStream->protocol("dot3")->configWidget()); + wl.append(mpStream->protocol("llc")->configWidget()); } else if (rbFtLlcSnap->isChecked()) { - wl.append(mpStream->protocolById(122)->configWidget()); - wl.append(mpStream->protocolById(123)->configWidget()); - wl.append(mpStream->protocolById(124)->configWidget()); + wl.append(mpStream->protocol("dot3")->configWidget()); + wl.append(mpStream->protocol("llc")->configWidget()); + wl.append(mpStream->protocol("snap")->configWidget()); } break; case 2: // L3 if (rbL3Ipv4->isChecked()) - wl.append(mpStream->protocolById(130)->configWidget()); + wl.append(mpStream->protocol("ip4")->configWidget()); break; case 3: // L4 if (rbL4Tcp->isChecked()) - wl.append(mpStream->protocolById(140)->configWidget()); + wl.append(mpStream->protocol("tcp")->configWidget()); else if (rbL4Udp->isChecked()) - wl.append(mpStream->protocolById(141)->configWidget()); + wl.append(mpStream->protocol("udp")->configWidget()); break; } - if (wl.size()) + if (wl.size()) + { layout = new QVBoxLayout; - for (int i=0; i < wl.size(); i++) - layout->addWidget(wl.at(i)); + for (int i=0; i < wl.size(); i++) + layout->addWidget(wl.at(i)); - twProto->widget(index)->setLayout(layout); + twProto->widget(index)->setLayout(layout); + } } void StreamConfigDialog::update_NumPacketsAndNumBursts() diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index f6c5a41..75cae99 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -29,6 +29,10 @@ public: private: //QList *mpStreamList; + QButtonGroup *bgFrameType; + QButtonGroup *bgL3Proto; + QButtonGroup *bgL4Proto; + Port& mPort; uint mCurrentStreamIndex; Stream *mpStream; @@ -45,7 +49,6 @@ private: static int lastProtoTabIndex; void setupUiExtra(); - void updateSelectedProtocols(); void LoadCurrentStream(); void StoreCurrentStream(Stream *pStream); @@ -54,18 +57,7 @@ private slots: void on_pbPrev_clicked(); void on_pbNext_clicked(); - void on_rbFtNone_toggled(bool checked); - void on_rbFtEthernet2_toggled(bool checked); - void on_rbFt802Dot3Raw_toggled(bool checked); - void on_rbFt802Dot3Llc_toggled(bool checked); - void on_rbFtLlcSnap_toggled(bool checked); - void on_rbL3Ipv4_toggled(bool checked); - void on_rbL3Arp_toggled(bool checked); - void on_rbL4Icmp_toggled(bool checked); - void on_rbL4Igmp_toggled(bool checked); - void on_rbL4Tcp_toggled(bool checked); - void on_rbL4Udp_toggled(bool checked); - + void updateSelectedProtocols(); void on_twTopLevel_currentChanged(int index); void on_twProto_currentChanged(int index); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index e410c81..9eba69a 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -59,7 +59,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - Frame Length (including CRC) + Frame Length (including FCS) @@ -159,164 +159,60 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Frame Type - + - - - - - - - None - - - true - - - - - - - Ethernet II - - - false - - - - - - - 802.3 Raw - - - - - - - 802.3 LLC - - - false - - - - - - - LLC SNAP - - - - - - - - - Qt::Vertical - - - - - - - - - DSAP - - - - - - - true - - - HH; - - - - - - - SSAP - - - - - - - true - - - HH; - - - - - - - Control - - - - - - - true - - - HH; - - - - - - - OUI - - - - - - - true - - - HH HH HH; - - - - - - - Type - - - - - - - true - - - HH HH; - - - - - - + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + LLC SNAP + + - + L3 @@ -358,7 +254,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + L4 @@ -416,7 +312,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Qt::Vertical diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 429c6a3..04d0d67 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -1,3 +1,4 @@ +#include #include "abstractprotocol.h" /*! @@ -18,19 +19,30 @@ - metaFieldCount() - isMetaField() */ -AbstractProtocol::AbstractProtocol(Stream *parent) +AbstractProtocol::AbstractProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : frameProtocols(frameProtoList) { + qDebug("%s: &frameproto = %p/%p (sz:%d)", __FUNCTION__, &frameProtocols, &frameProtoList, frameProtocols.size()); stream = parent; metaCount = -1; + protoSize = -1; } AbstractProtocol::~AbstractProtocol() { } +AbstractProtocol* AbstractProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return NULL; +} /*! - \fn virtual void protoDataCopyInto(OstProto::Stream &stream) = 0; + \fn virtual void protoDataCopyInto(OstProto::OstProto::StreamCore &stream) = 0; Copy the protocol's protobuf into the passed in stream \n In the base class this is a pure virtual function. Subclasses should @@ -38,7 +50,7 @@ AbstractProtocol::~AbstractProtocol() stream.AddExtension()->CopyFrom() */ /* - \fn virtual void protoDataCopyFrom(const OstProto::Stream &stream) = 0; + \fn virtual void protoDataCopyFrom(const OstProto::OstProto::StreamCore &stream) = 0; FIXME */ /*! Returns the full name of the protocol \n @@ -114,6 +126,11 @@ int AbstractProtocol::frameFieldCount() const the field's value e.g. a checksum field may include "(correct)" or "(incorrect)" alongwith the actual checksum value. \n The default implementation returns FIXME + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recrusion - + - protocolFrameCksum() */ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const @@ -151,6 +168,81 @@ bool AbstractProtocol::setFieldData(int index, const QVariant &value, return false; } +quint32 AbstractProtocol::protocolId(ProtocolIdType type) const +{ + return 0; +} + +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id = 0xFFFFFFFF; + QLinkedListIterator iter(frameProtocols); + + if (iter.findNext(this)) + { + if (iter.hasNext()) + id = iter.next()->protocolId(type); + } + + qDebug("%s: payloadProtocolId = %u", __FUNCTION__, id); + return id; +} + +int AbstractProtocol::protocolFrameSize() const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (!fieldData(i, FieldIsMeta).toBool()) + bitsize += fieldData(i, FieldBitSize).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +int AbstractProtocol::protocolFrameOffset() const +{ + int size = 0; + QLinkedListIterator iter(frameProtocols); + + if (iter.findNext(this)) + { + iter.previous(); + while (iter.hasPrevious()) + size += iter.previous()->protocolFrameSize(); + } + else + return -1; + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +int AbstractProtocol::protocolFramePayloadSize() const +{ + int size = 0; + + QLinkedListIterator iter(frameProtocols); + + if (iter.findNext(this)) + { + while (iter.hasNext()) + size += iter.next()->protocolFrameSize(); + } + else + return -1; + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + /*! Returns a byte array encoding the protocol (and its fields) which can be inserted into the stream's frame The default implementation forms and returns an ordered concatenation of @@ -223,3 +315,53 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex) const return proto; } +QVariant AbstractProtocol::protocolFrameCksum() const +{ + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(-1); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return qFromBigEndian((quint16) ~sum); +} + +QVariant AbstractProtocol::protocolFramePayloadCksum() const +{ + int cksum = 0; + QLinkedListIterator iter(frameProtocols); + + if (iter.findNext(this)) + { + while (iter.hasNext()) + cksum += iter.next()->protocolFrameCksum().toUInt(); // TODO: chg to partial + } + else + return -1; +#if 0 + while(cksum>>16) + cksum = (cksum & 0xFFFF) + (cksum >> 16); + + return (quint16) ~cksum; +#endif + + return cksum; +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 7c7255e..ec0584b 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -5,7 +5,9 @@ #include #include #include +#include +//#include "../rpc/pbhelper.h" #include "../common/protocol.pb.h" #define BASE_BIN (2) @@ -13,16 +15,24 @@ #define BASE_DEC (10) #define BASE_HEX (16) -class Stream; +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class OstProto::StreamCore; +class AbstractProtocol; + +typedef QLinkedList ProtocolList; class AbstractProtocol { private: mutable int metaCount; + mutable int protoSize; mutable QString protoAbbr; protected: - Stream *stream; + OstProto::StreamCore *stream; + ProtocolList &frameProtocols; public: enum FieldAttrib { @@ -34,15 +44,29 @@ public: FieldIsMeta //! bool indicating if field is meta }; - AbstractProtocol(Stream *parent = 0); + enum ProtocolIdType { + ProtocolIdLlc, + ProtocolIdEth, + ProtocolIdIp, + }; + + AbstractProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~AbstractProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream) = 0; virtual void protoDataCopyFrom(const OstProto::Stream &stream) = 0; virtual QString name() const; virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + virtual int fieldCount() const; virtual int metaFieldCount() const; int frameFieldCount() const; @@ -53,6 +77,12 @@ public: FieldAttrib attrib = FieldValue); QByteArray protocolFrameValue(int streamIndex = 0) const; + int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; + + virtual QVariant protocolFrameCksum() const; + QVariant protocolFramePayloadCksum() const; virtual QWidget* configWidget() = 0; virtual void loadConfigWidget() = 0; diff --git a/common/dot3.cpp b/common/dot3.cpp index 67b70d9..448c0c5 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -3,6 +3,8 @@ #include "Dot3.h" +#define SZ_FCS 4 + Dot3ConfigForm *Dot3Protocol::configForm = NULL; Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) @@ -11,8 +13,10 @@ Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) setupUi(this); } -Dot3Protocol::Dot3Protocol(Stream *parent) - : AbstractProtocol(parent) +Dot3Protocol::Dot3Protocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new Dot3ConfigForm; @@ -22,6 +26,13 @@ Dot3Protocol::~Dot3Protocol() { } +AbstractProtocol* Dot3Protocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new Dot3Protocol(frameProtoList, streamCore); +} + void Dot3Protocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -61,14 +72,27 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("Length"); case FieldValue: - return data.length(); + { + quint16 len; + + len = stream->frame_len() - SZ_FCS; + return len; + } case FieldTextValue: - return QString("%1").arg(data.length()); + { + quint16 len; + + len = stream->frame_len() - SZ_FCS; + return QString("%1").arg(len); + } case FieldFrameValue: { + quint16 len; QByteArray fv; + + len = stream->frame_len() - SZ_FCS; fv.resize(2); - qToBigEndian((quint16) data.length(), (uchar*) fv.data()); + qToBigEndian(len, (uchar*) fv.data()); return fv; } default: @@ -98,7 +122,8 @@ QWidget* Dot3Protocol::configWidget() void Dot3Protocol::loadConfigWidget() { - configForm->leLength->setText(QString().setNum(data.length())); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); } void Dot3Protocol::storeConfigWidget() diff --git a/common/dot3.h b/common/dot3.h index e49f312..3b98b24 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -26,9 +26,14 @@ private: }; public: - Dot3Protocol(Stream *parent = 0); + Dot3Protocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~Dot3Protocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); diff --git a/common/eth2.cpp b/common/eth2.cpp index caaf75b..9fbfda6 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -11,8 +11,10 @@ Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) setupUi(this); } -Eth2Protocol::Eth2Protocol(Stream *parent) - : AbstractProtocol(parent) +Eth2Protocol::Eth2Protocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new Eth2ConfigForm; @@ -22,6 +24,13 @@ Eth2Protocol::~Eth2Protocol() { } +AbstractProtocol* Eth2Protocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new Eth2Protocol(frameProtoList, streamCore); +} + void Eth2Protocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -56,26 +65,31 @@ QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, switch (index) { case eth2_type: + { + quint16 type; switch(attrib) { case FieldName: return QString("Type"); case FieldValue: - return data.type(); + type = payloadProtocolId(ProtocolIdEth); + return type; case FieldTextValue: - return QString("%1").arg(data.type(), 16); + type = payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; + type = payloadProtocolId(ProtocolIdEth); fv.resize(2); - qToBigEndian((quint16) data.type(), (uchar*) fv.data()); + qToBigEndian((quint16) type, (uchar*) fv.data()); return fv; } default: break; } break; - + } default: break; } @@ -112,12 +126,8 @@ QWidget* Eth2Protocol::configWidget() void Eth2Protocol::loadConfigWidget() { -#define uintToHexStr(num, bytesize) \ - QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) - - configForm->leType->setText(uintToHexStr(data.type(), 2)); - -#undef uintToHexStr + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); } void Eth2Protocol::storeConfigWidget() diff --git a/common/eth2.h b/common/eth2.h index 580b712..e71659d 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -26,9 +26,14 @@ private: }; public: - Eth2Protocol(Stream *parent = 0); + Eth2Protocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~Eth2Protocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); diff --git a/common/eth2.ui b/common/eth2.ui index 9099dbb..a79068c 100644 --- a/common/eth2.ui +++ b/common/eth2.ui @@ -32,10 +32,10 @@ - true + false - HH HH; + >HH HH; diff --git a/common/ip4.cpp b/common/ip4.cpp index 625d3c7..260c720 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -44,9 +44,16 @@ void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) } } -Ip4Protocol::Ip4Protocol(Stream *parent) - : AbstractProtocol(parent) +Ip4Protocol::Ip4Protocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { +#if 0 + PbHelper pbh; + + pbh.ForceSetSingularDefault(&data); +#endif if (configForm == NULL) configForm = new Ip4ConfigForm; } @@ -55,6 +62,13 @@ Ip4Protocol::~Ip4Protocol() { } +AbstractProtocol* Ip4Protocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new Ip4Protocol(frameProtoList, streamCore); +} + void Ip4Protocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -78,6 +92,19 @@ QString Ip4Protocol::shortName() const return QString("IPv4"); } +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + int Ip4Protocol::fieldCount() const { return ip4_fieldCount; @@ -139,25 +166,42 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, } break; case ip4_totLen: + { switch(attrib) { case FieldName: return QString("Total Length"); case FieldValue: - return data.totlen(); + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize() + 20); + return totlen; + } case FieldFrameValue: { QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize() + 20); fv.resize(2); - qToBigEndian((quint16) data.totlen(), (uchar*) fv.data()); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } case FieldTextValue: - return QString("%1").arg(data.totlen()); + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize() + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; default: break; } break; + } case ip4_id: switch(attrib) { @@ -244,42 +288,86 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, } break; case ip4_proto: + { switch(attrib) { case FieldName: return QString("Protocol"); case FieldValue: - return data.proto(); + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return id; + } case FieldFrameValue: - return QByteArray(1, (char)data.proto()); + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } case FieldTextValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); return QString("0x%1"). - arg(data.proto(), 2, BASE_HEX, QChar('0')); + arg(id, 2, BASE_HEX, QChar('0')); + } default: break; } break; + } case ip4_cksum: + { + switch(attrib) { case FieldName: return QString("Header Checksum"); case FieldValue: - return data.cksum(); + { + quint16 cksum; + + if (streamIndex < 0) + cksum = 0; + else if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum().toUInt(); + return cksum; + } case FieldFrameValue: { QByteArray fv; + quint16 cksum; + + if (streamIndex < 0) + cksum = 0; + else if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum().toUInt(); fv.resize(2); - qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: + { + quint16 cksum; + + if (streamIndex < 0) + cksum = 0; + else if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum().toUInt(); return QString("0x%1"). - arg(data.cksum(), 4, BASE_HEX, QChar('0'));; + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; default: break; } break; + } case ip4_srcAddr: switch(attrib) { @@ -381,27 +469,27 @@ QWidget* Ip4Protocol::configWidget() void Ip4Protocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) configForm->leIpVersion->setText(QString().setNum(data.ver_hdrlen() >> 4)); configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); configForm->leIpHdrLen->setText(QString().setNum(data.ver_hdrlen() & 0x0F)); configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); - configForm->leIpTos->setText(uintToHexStr(data.tos(), QString(), 1)); - - configForm->leIpLength->setText(QString().setNum(data.totlen())); + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); - configForm->leIpId->setText(uintToHexStr(data.id(), QString(), 2)); + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); configForm->leIpTtl->setText(QString().setNum(data.ttl())); - configForm->leIpProto->setText(uintToHexStr(data.proto(), QString(), 1)); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); - configForm->leIpCksum->setText(uintToHexStr(data.cksum(), QString(), 2)); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); diff --git a/common/ip4.h b/common/ip4.h index 73d3004..1bf8ccc 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -58,15 +58,20 @@ private: }; public: - Ip4Protocol(Stream *parent = 0); + Ip4Protocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~Ip4Protocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); virtual QString name() const; virtual QString shortName() const; - + virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/ip4.ui b/common/ip4.ui index 35a85bf..785e7e3 100644 --- a/common/ip4.ui +++ b/common/ip4.ui @@ -28,7 +28,7 @@ false - 4 +
@@ -45,7 +45,7 @@ false - 5 + @@ -59,7 +59,7 @@ - HH; + >HH; @@ -100,7 +100,7 @@ - HH HH; + >HH HH; @@ -149,7 +149,7 @@ - 64 + @@ -183,7 +183,7 @@ false - HH HH; + >HH HH; @@ -291,8 +291,11 @@ false + + 009.009.009.009; + - 255.255.255.255 + ... @@ -355,8 +358,11 @@ false + + 009.009.009.009; + - 255.255.255.255 + ... diff --git a/common/llc.cpp b/common/llc.cpp index 394ca2c..c0183ca 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -11,8 +11,10 @@ LlcConfigForm::LlcConfigForm(QWidget *parent) setupUi(this); } -LlcProtocol::LlcProtocol(Stream *parent) - : AbstractProtocol(parent) +LlcProtocol::LlcProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new LlcConfigForm; @@ -22,6 +24,13 @@ LlcProtocol::~LlcProtocol() { } +AbstractProtocol* LlcProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new LlcProtocol(frameProtoList, streamCore); +} + void LlcProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -53,6 +62,14 @@ int LlcProtocol::fieldCount() const QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = (id >> 16) & 0xFF; + ssap = (id >> 8) & 0xFF; + ctl = (id >> 0) & 0xFF; + switch (index) { case llc_dsap: @@ -61,11 +78,11 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("DSAP"); case FieldValue: - return data.dsap(); + return dsap; case FieldTextValue: - return QString("%1").arg(data.dsap(), BASE_HEX); + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); case FieldFrameValue: - return QByteArray(1, (char)(data.dsap())); + return QByteArray(1, (char)(dsap)); default: break; } @@ -74,13 +91,13 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("DSAP"); + return QString("SSAP"); case FieldValue: - return data.ssap(); + return ssap; case FieldTextValue: - return QString("%1").arg(data.ssap(), BASE_HEX); + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); case FieldFrameValue: - return QByteArray(1, (char)(data.ssap())); + return QByteArray(1, (char)(ssap)); default: break; } @@ -89,13 +106,13 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("DSAP"); + return QString("Control"); case FieldValue: - return data.ctl(); + return ctl; case FieldTextValue: - return QString("%1").arg(data.ctl(), BASE_HEX); + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); case FieldFrameValue: - return QByteArray(1, (char)(data.ctl())); + return QByteArray(1, (char)(ctl)); default: break; } @@ -123,9 +140,16 @@ QWidget* LlcProtocol::configWidget() void LlcProtocol::loadConfigWidget() { - configForm->leDsap->setText(QString("%1").arg(data.dsap(), 2, BASE_HEX, QChar('0'))); - configForm->leSsap->setText(QString("%1").arg(data.ssap(), 2, BASE_HEX, QChar('0'))); - configForm->leControl->setText(QString("%1").arg(data.ctl(), 2, BASE_HEX, QChar('0'))); +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr } void LlcProtocol::storeConfigWidget() diff --git a/common/llc.h b/common/llc.h index 18e1181..8403fab 100644 --- a/common/llc.h +++ b/common/llc.h @@ -31,9 +31,14 @@ private: }; public: - LlcProtocol(Stream *parent = 0); + LlcProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~LlcProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); diff --git a/common/llc.ui b/common/llc.ui index 4518b3d..5f305a0 100644 --- a/common/llc.ui +++ b/common/llc.ui @@ -38,10 +38,10 @@ - true + false - HH; + >HH; @@ -58,10 +58,10 @@ - true + false - HH; + >HH; @@ -78,10 +78,10 @@ - true + false - HH; + >HH; diff --git a/common/mac.cpp b/common/mac.cpp index 59b2607..53defa7 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -46,8 +46,10 @@ void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) } -MacProtocol::MacProtocol(Stream *parent) - : AbstractProtocol(parent) +MacProtocol::MacProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new MacConfigForm; @@ -57,6 +59,13 @@ MacProtocol::~MacProtocol() { } +AbstractProtocol* MacProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new MacProtocol(frameProtoList, streamCore); +} + void MacProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -174,13 +183,12 @@ QWidget* MacProtocol::configWidget() void MacProtocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) - configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), str, 6)); + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); - configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), QString(), 6)); + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); diff --git a/common/mac.h b/common/mac.h index 2842475..575b9b1 100644 --- a/common/mac.h +++ b/common/mac.h @@ -39,9 +39,14 @@ private: }; public: - MacProtocol(Stream *parent = 0); + MacProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~MacProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); diff --git a/common/mac.ui b/common/mac.ui index 9095f29..9afb006 100644 --- a/common/mac.ui +++ b/common/mac.ui @@ -49,7 +49,7 @@ - HH HH HH HH HH HH; + >HH HH HH HH HH HH; @@ -111,7 +111,7 @@ - HH HH HH HH HH HH; + >HH HH HH HH HH HH; diff --git a/common/ostproto.pro b/common/ostproto.pro index 3bd8e9c..59b2453 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -26,6 +26,9 @@ PROTOS += \ udp.proto HEADERS += \ abstractprotocol.h \ + protocolmanager.h \ + protocolcollection.h \ + streambase.h \ mac.h \ payload.h \ eth2.h \ @@ -37,6 +40,9 @@ HEADERS += \ udp.h SOURCES += \ abstractprotocol.cpp \ + protocolmanager.cpp \ + protocolcollection.cpp \ + streambase.cpp \ mac.cpp \ payload.cpp \ eth2.cpp \ diff --git a/common/payload.cpp b/common/payload.cpp index 8feecf9..8e79c7d 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -1,9 +1,11 @@ #include #include -#include "../client/stream.h" +//#include "../client/stream.h" #include "payload.h" +#define SZ_FCS 4 + PayloadConfigForm *PayloadProtocol::configForm = NULL; PayloadConfigForm::PayloadConfigForm(QWidget *parent) @@ -29,8 +31,10 @@ void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) } } -PayloadProtocol::PayloadProtocol(Stream *parent) - : AbstractProtocol(parent) +PayloadProtocol::PayloadProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new PayloadConfigForm; @@ -40,6 +44,13 @@ PayloadProtocol::~PayloadProtocol() { } +AbstractProtocol* PayloadProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new PayloadProtocol(frameProtoList, streamCore); +} + void PayloadProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -88,10 +99,8 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; int dataLen; - // FIXME: cannot use stream since it is only on client not - // on server - //dataLen = stream->frameLen() - stream->protocolHeaderSize(); - dataLen = 64; + dataLen = stream->frame_len() - protocolFrameOffset(); + dataLen -= SZ_FCS; fv.resize(dataLen+4); switch(data.pattern_mode()) { @@ -159,10 +168,8 @@ QWidget* PayloadProtocol::configWidget() void PayloadProtocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) - configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); - configForm->lePattern->setText(uintToHexStr(data.pattern(), QString(), 4)); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); } void PayloadProtocol::storeConfigWidget() diff --git a/common/payload.h b/common/payload.h index aa66f5d..4a1dc03 100644 --- a/common/payload.h +++ b/common/payload.h @@ -31,9 +31,14 @@ private: }; public: - PayloadProtocol(Stream *parent = 0); + PayloadProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~PayloadProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); diff --git a/common/payload.ui b/common/payload.ui index 9de7ce1..c0a1b31 100644 --- a/common/payload.ui +++ b/common/payload.ui @@ -46,7 +46,7 @@ - HH HH HH HH; + >HH HH HH HH; diff --git a/common/protocolcollection.cpp b/common/protocolcollection.cpp new file mode 100644 index 0000000..60e6d21 --- /dev/null +++ b/common/protocolcollection.cpp @@ -0,0 +1,106 @@ +#include "protocolcollection.h" + +extern ProtocolManager OstProtocolManager; + +ProtocolCollection::ProtocolCollection(ProtocolList &streamProtocols, + OstProto::StreamCore *streamCore) + : protoManager(OstProtocolManager) +{ + // Create an instance of each registered protocol + + QMapIterator iter(protoManager.factory); + + while (iter.hasNext()) + { + AbstractProtocol* (*p)(ProtocolList&, OstProto::StreamCore*); + AbstractProtocol* q; + + iter.next(); + p = (AbstractProtocol* (*)(ProtocolList&, OstProto::StreamCore*)) + iter.value(); + q = (*p)(streamProtocols, streamCore); + + protocols.insert(iter.key(), q); + } +} + +ProtocolCollection::~ProtocolCollection() +{ + QMutableMapIterator iter(protocols); + + while (iter.hasNext()) + { + iter.next(); + if (iter.value()) + { + delete iter.value(); + iter.remove(); + } + } +} + +void ProtocolCollection::protoDataCopyFrom(const OstProto::Stream &stream) const +{ + QMapIterator iter(protocols); + + while (iter.hasNext()) + { + iter.next(); + if (iter.value()) + { + iter.value()->protoDataCopyFrom(stream); + } + } +} + +void ProtocolCollection::protoDataCopyInto(OstProto::Stream &stream) const +{ + QMapIterator iter(protocols); + + while (iter.hasNext()) + { + iter.next(); + if (iter.value()) + { + iter.value()->protoDataCopyInto(stream); + } + } +} + +void ProtocolCollection::loadConfigWidgets() const +{ + QMapIterator iter(protocols); + + while (iter.hasNext()) + { + iter.next(); + if (iter.value()) + { + iter.value()->loadConfigWidget(); + } + } +} + +void ProtocolCollection::storeConfigWidgets() const +{ + QMapIterator iter(protocols); + + while (iter.hasNext()) + { + iter.next(); + if (iter.value()) + { + iter.value()->storeConfigWidget(); + } + } +} + +AbstractProtocol* ProtocolCollection::protocol(int protoNum) +{ + return protocols.value(protoNum); +} + +AbstractProtocol* ProtocolCollection::protocol(QString protoName) +{ + return protocols.value(protoManager.nameToNumberMap.value(protoName)); +} diff --git a/common/protocolcollection.h b/common/protocolcollection.h new file mode 100644 index 0000000..61ca418 --- /dev/null +++ b/common/protocolcollection.h @@ -0,0 +1,30 @@ +#ifndef _PROTOCOL_COLLECTION_H +#define _PROTOCOL_COLLECTION_H + +#include +#include + +#include "abstractprotocol.h" +#include "protocolmanager.h" + +class ProtocolCollection { + + ProtocolManager &protoManager; + QMap protocols; + +public: + ProtocolCollection(ProtocolList &streamProtocols, + OstProto::StreamCore *streamCore); + ProtocolCollection::~ProtocolCollection(); + + void protoDataCopyFrom(const OstProto::Stream &stream) const; + void protoDataCopyInto(OstProto::Stream &stream) const; + + void loadConfigWidgets() const; + void storeConfigWidgets() const; + + AbstractProtocol* protocol(int protoNum); + AbstractProtocol* protocol(QString protoName); +}; + +#endif diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..7b49e33 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,38 @@ +#include "protocolmanager.h" + +#include "mac.h" +#include "payload.h" + +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "ip4.h" +#include "tcp.h" +#include "udp.h" + +QMap ProtocolManager::factory; +QMap ProtocolManager::nameToNumberMap; + +ProtocolManager OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + registerProtocol(51, QString("mac"), (void*) MacProtocol::createInstance); + registerProtocol(52, QString("payload"), (void*) PayloadProtocol::createInstance); + registerProtocol(121, QString("eth2"), (void*) Eth2Protocol::createInstance); + registerProtocol(122, QString("dot3"), (void*) Dot3Protocol::createInstance); + registerProtocol(123, QString("llc"), (void*) LlcProtocol::createInstance); + registerProtocol(124, QString("snap"), (void*) SnapProtocol::createInstance); + registerProtocol(130, QString("ip4"), (void*) Ip4Protocol::createInstance); + registerProtocol(140, QString("tcp"), (void*) TcpProtocol::createInstance); + registerProtocol(141, QString("udp"), (void*) UdpProtocol::createInstance); +} + +void ProtocolManager::registerProtocol(int protoNumber, QString protoName, + void *protoInstanceCreator) +{ + // TODO: validate incoming params for duplicates with existing + nameToNumberMap.insert(protoName, protoNumber); + factory.insert(protoNumber, protoInstanceCreator); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..5d8e1ca --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,18 @@ +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include + +class ProtocolManager +{ +public: + static QMap nameToNumberMap; + static QMap factory; + +public: + ProtocolManager(); + void registerProtocol(int protoNumber, QString protoName, + void *protoCreator); +}; + +#endif diff --git a/common/snap.cpp b/common/snap.cpp index 33f841e..46cca1e 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -11,8 +11,10 @@ SnapConfigForm::SnapConfigForm(QWidget *parent) setupUi(this); } -SnapProtocol::SnapProtocol(Stream *parent) - : AbstractProtocol(parent) +SnapProtocol::SnapProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new SnapConfigForm; @@ -22,6 +24,13 @@ SnapProtocol::~SnapProtocol() { } +AbstractProtocol* SnapProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new SnapProtocol(frameProtoList, streamCore); +} + void SnapProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -45,6 +54,17 @@ QString SnapProtocol::shortName() const return QString("SNAP"); } +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + int SnapProtocol::fieldCount() const { return snap_fieldCount; @@ -76,7 +96,33 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, break; } break; + case snap_type: + { + quint16 type; + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } default: break; } @@ -99,14 +145,17 @@ QWidget* SnapProtocol::configWidget() void SnapProtocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) - configForm->leOui->setText(uintToHexStr(data.oui(), str, 3)); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); } void SnapProtocol::storeConfigWidget() { bool isOk; - data.set_oui(configForm->leOui->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); + data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); } diff --git a/common/snap.h b/common/snap.h index 9913b0f..ba0a3b6 100644 --- a/common/snap.h +++ b/common/snap.h @@ -21,19 +21,26 @@ private: enum snapfield { snap_oui = 0, + snap_type, snap_fieldCount }; public: - SnapProtocol(Stream *parent = 0); + SnapProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~SnapProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); virtual QString name() const; virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; diff --git a/common/snap.proto b/common/snap.proto index b6c310c..c5ea432 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -4,7 +4,7 @@ package OstProto; message Snap { optional uint32 oui = 1; - //optional uint32 type = 2; + optional uint32 type = 2; } extend Stream { diff --git a/common/snap.ui b/common/snap.ui index 1f2b789..9d0e4a5 100644 --- a/common/snap.ui +++ b/common/snap.ui @@ -29,10 +29,10 @@ - true + false - HH HH HH; + >HH HH HH; @@ -46,10 +46,10 @@ - true + false - HH HH; + >HH HH; diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..4bd4faf --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,69 @@ +#include "streambase.h" + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl), + protocols(currentFrameProtocols, mCore) +{ + mStreamId->set_id(0xFFFFFFFF); +} + +StreamBase::~StreamBase() +{ + delete mStreamId; + delete mCore; + delete mControl; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + protocols.protoDataCopyFrom(stream); + setFrameProtocol(frameProtocol()); +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + protocols.protoDataCopyInto(stream); +} + +QList StreamBase::frameProtocol() +{ + QList protocolList; + + for (int i = 0; i < mCore->frame_proto_size(); i++) + protocolList.append(mCore->frame_proto(i)); + + return protocolList; +} + +void StreamBase::setFrameProtocol(QList protocolList) +{ + mCore->clear_frame_proto(); + currentFrameProtocols.clear(); + + for (int i = 0; i < protocolList.size(); i++) + { + mCore->add_frame_proto(protocolList.at(i)); + currentFrameProtocols.append(protocols.protocol(protocolList.at(i))); + } +} + +AbstractProtocol* StreamBase::protocol(int protoNum) +{ + return protocols.protocol(protoNum); +} + +AbstractProtocol* StreamBase::protocol(QString protoName) +{ + return protocols.protocol(protoName); +} + diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..c8eb2c8 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,36 @@ +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include + +#include "protocolcollection.h" + +class StreamBase +{ +protected: // TODO: temp - make private + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + +private: + ProtocolList currentFrameProtocols; +protected: + ProtocolCollection protocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + QList frameProtocol(); + void setFrameProtocol(QList protocolList); + + AbstractProtocol* protocol(int protoNum); + AbstractProtocol* protocol(QString protoName); + + // TODO: make a copy constructor +}; + +#endif diff --git a/common/tcp.cpp b/common/tcp.cpp index 4c81c1c..26b14c0 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -11,8 +11,10 @@ TcpConfigForm::TcpConfigForm(QWidget *parent) setupUi(this); } -TcpProtocol::TcpProtocol(Stream *parent) - : AbstractProtocol(parent) +TcpProtocol::TcpProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new TcpConfigForm; @@ -22,6 +24,13 @@ TcpProtocol::~TcpProtocol() { } +AbstractProtocol* TcpProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new TcpProtocol(frameProtoList, streamCore); +} + void TcpProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -45,6 +54,17 @@ QString TcpProtocol::shortName() const return QString("TCP"); } +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + int TcpProtocol::fieldCount() const { return tcp_fieldCount; @@ -149,7 +169,7 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, case FieldTextValue: return QString("%1").arg((data.hdrlen_rsvd() >> 4) & 0x0F); case FieldFrameValue: - return QByteArray(1, (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0xF0)); case FieldBitSize: return 4; default: @@ -303,7 +323,6 @@ QWidget* TcpProtocol::configWidget() void TcpProtocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); diff --git a/common/tcp.h b/common/tcp.h index 9fc2367..0637fc1 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -45,14 +45,20 @@ private: }; public: - TcpProtocol(Stream *parent = 0); + TcpProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~TcpProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); virtual QString name() const; virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; diff --git a/common/tcp.ui b/common/tcp.ui index 4a333be..e19e9fb 100644 --- a/common/tcp.ui +++ b/common/tcp.ui @@ -43,7 +43,7 @@ false - HH HH; + >HH HH; diff --git a/common/udp.cpp b/common/udp.cpp index c2e1bfe..4d8585a 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -11,8 +11,10 @@ UdpConfigForm::UdpConfigForm(QWidget *parent) setupUi(this); } -UdpProtocol::UdpProtocol(Stream *parent) - : AbstractProtocol(parent) +UdpProtocol::UdpProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) { if (configForm == NULL) configForm = new UdpConfigForm; @@ -22,6 +24,13 @@ UdpProtocol::~UdpProtocol() { } +AbstractProtocol* UdpProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new UdpProtocol(frameProtoList, streamCore); +} + void UdpProtocol::protoDataCopyInto(OstProto::Stream &stream) { // FIXME: multiple headers @@ -45,6 +54,17 @@ QString UdpProtocol::shortName() const return QString("UDP"); } +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + int UdpProtocol::fieldCount() const { return udp_fieldCount; @@ -98,26 +118,47 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, break; case udp_totLen: + { + switch(attrib) { case FieldName: return QString("Datagram Length"); case FieldValue: - return data.totlen(); - case FieldTextValue: - return QString("%1").arg(data.totlen()); + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize() + 8); + return totlen; + } case FieldFrameValue: { QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize() + 8); fv.resize(2); - qToBigEndian((quint16) data.totlen(), (uchar*) fv.data()); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize() + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; default: break; } break; - + } case udp_cksum: switch(attrib) { @@ -173,7 +214,6 @@ QWidget* UdpProtocol::configWidget() void UdpProtocol::loadConfigWidget() { -#define uintToHexStr(num, str, size) QString().setNum(num, 16) configForm->leUdpSrcPort->setText(QString().setNum(data.src_port())); configForm->leUdpDstPort->setText(QString().setNum(data.dst_port())); diff --git a/common/udp.h b/common/udp.h index 4fe5147..5c40330 100644 --- a/common/udp.h +++ b/common/udp.h @@ -32,14 +32,20 @@ private: }; public: - UdpProtocol(Stream *parent = 0); + UdpProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); virtual ~UdpProtocol(); + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + virtual void protoDataCopyInto(OstProto::Stream &stream); virtual void protoDataCopyFrom(const OstProto::Stream &stream); virtual QString name() const; virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; diff --git a/common/udp.ui b/common/udp.ui index e8e29d3..c5c9862 100644 --- a/common/udp.ui +++ b/common/udp.ui @@ -62,7 +62,7 @@ false - HH HH; + >HH HH; @@ -97,5 +97,38 @@ - + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 149 + 63 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 158 + 106 + + + +
diff --git a/common/vlan.ui b/common/vlan.ui index 0ae3be6..c35dc89 100644 --- a/common/vlan.ui +++ b/common/vlan.ui @@ -126,7 +126,7 @@ true - HH HH; + >HH HH; diff --git a/server/myservice.cpp b/server/myservice.cpp index 20bbda0..546fda5 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -4,17 +4,6 @@ #include #include -#include "../common/mac.h" -#include "../common/payload.h" - -#include "../common/eth2.h" // FIXME: proto DB -#include "../common/dot3.h" // FIXME: proto DB -#include "../common/llc.h" // FIXME: proto DB -#include "../common/snap.h" // FIXME: proto DB -#include "../common/ip4.h" // FIXME: proto DB -#include "../common/tcp.h" // FIXME: proto DB -#include "../common/udp.h" // FIXME: proto DB - #if 0 #include #include @@ -25,62 +14,19 @@ StreamInfo::StreamInfo() { +#if 0 PbHelper pbh; - pbh.ForceSetSingularDefault(&mCore); - pbh.ForceSetSingularDefault(&mControl); - - mProtocolList.append(new MacProtocol); - mProtocolList.append(new PayloadProtocol()); - - // FIXME: proto DB - mProtocolList.append(new Eth2Protocol); - mProtocolList.append(new Dot3Protocol); - mProtocolList.append(new LlcProtocol); - mProtocolList.append(new SnapProtocol); - mProtocolList.append(new Ip4Protocol); - mProtocolList.append(new TcpProtocol); - mProtocolList.append(new UdpProtocol); + pbh.ForceSetSingularDefault(mCore); + pbh.ForceSetSingularDefault(mControl); +#endif } StreamInfo::~StreamInfo() { - for (int i = 0; i < mProtocolList.size(); i++) - delete mProtocolList.at(i); -} - -AbstractProtocol* StreamInfo::protocolById(int id) -{ - // FIXME BAD BAD VERY BAD! - switch(id) { - case 51: - return mProtocolList.at(0); - case 52: - return mProtocolList.at(1); - case 121: - return mProtocolList.at(2); - case 122: - return mProtocolList.at(3); - case 123: - return mProtocolList.at(4); - case 124: - return mProtocolList.at(5); - // case 125 (unused) -#if 0 // todo VLAN - case 126: - return mProtocolList.at(x); -#endif - case 130: - return mProtocolList.at(6); - case 140: - return mProtocolList.at(7); - case 141: - return mProtocolList.at(8); - default: - return NULL; - } } +#if 0 quint32 StreamInfo::pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, quint8 protocol, quint16 len) { @@ -96,78 +42,34 @@ quint32 StreamInfo::pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, // Above calculation done assuming 'big endian' - so convert to host order return qFromBigEndian(sum); } +#endif -quint32 StreamInfo::ipv4CksumPartial(uchar *buf, int len) -{ - quint32 sum = 0; - quint16 *ip = (quint16*) buf; - - if (len & 0x0001) - { - qFatal("Cannot calculate partial checksum on non multiple of 2 length"); - return 0; - } - - while(len) - { - sum += *ip; - if(sum & 0x80000000) - sum = (sum & 0xFFFF) + (sum >> 16); - ip++; - len -= 2; - } - - return sum; -} - -quint16 StreamInfo::ipv4Cksum(uchar *buf, int len, quint32 partialSum) -{ - quint32 sum = partialSum; - quint16 *ip = (quint16*) buf; - - while(len > 1) - { - sum += *ip; - if(sum & 0x80000000) - sum = (sum & 0xFFFF) + (sum >> 16); - ip++; - len -= 2; - } - - if (len) - sum += (unsigned short) *(unsigned char *)ip; - - while(sum>>16) - sum = (sum & 0xFFFF) + (sum >> 16); - - return (quint16) ~sum; -} int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) { int pktLen, len = 0; // Decide a frame length based on length mode - switch(mCore.len_mode()) + switch(mCore->len_mode()) { case OstProto::StreamCore::e_fl_fixed: - pktLen = mCore.frame_len(); + pktLen = mCore->frame_len(); break; case OstProto::StreamCore::e_fl_inc: - pktLen = mCore.frame_len_min() + (n % - (mCore.frame_len_max() - mCore.frame_len_min() + 1)); + pktLen = mCore->frame_len_min() + (n % + (mCore->frame_len_max() - mCore->frame_len_min() + 1)); break; case OstProto::StreamCore::e_fl_dec: - pktLen = mCore.frame_len_max() - (n % - (mCore.frame_len_max() - mCore.frame_len_min() + 1)); + pktLen = mCore->frame_len_max() - (n % + (mCore->frame_len_max() - mCore->frame_len_min() + 1)); break; case OstProto::StreamCore::e_fl_random: - pktLen = mCore.frame_len_min() + (qrand() % - (mCore.frame_len_max() - mCore.frame_len_min() + 1)); + pktLen = mCore->frame_len_min() + (qrand() % + (mCore->frame_len_max() - mCore->frame_len_min() + 1)); break; default: qWarning("Unhandled len mode %d. Using default 64", - mCore.len_mode()); + mCore->len_mode()); pktLen = 64; break; } @@ -179,14 +81,11 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) return 0; // FIXME: Calculated pktLen is an input to Payload Protocol - - // FIXME: checksums!!! - - for (int i = 0; i < mCore.frame_proto_size(); i++) + for (int i = 0; i < mCore->frame_proto_size(); i++) { QByteArray ba; - ba = protocolById(mCore.frame_proto(i))->protocolFrameValue(n); + ba = protocol(mCore->frame_proto(i))->protocolFrameValue(n); if (len + ba.size() < bufMaxSize) { memcpy(buf+len, ba.constData(), ba.size()); @@ -690,28 +589,28 @@ void PortInfo::update() for (int i = 0; i < streamList.size(); i++) { //_restart: - if (streamList[i]->mCore.is_enabled()) + if (streamList[i]->mCore->is_enabled()) { long numPackets, numBursts; long ibg, ipg; - switch (streamList[i]->mControl.unit()) + switch (streamList[i]->mControl->unit()) { case OstProto::StreamControl::e_su_bursts: - numBursts = streamList[i]->mControl.num_bursts(); - numPackets = streamList[i]->mControl.packets_per_burst(); - ibg = 1000000/streamList[i]->mControl.bursts_per_sec(); + numBursts = streamList[i]->mControl->num_bursts(); + numPackets = streamList[i]->mControl->packets_per_burst(); + ibg = 1000000/streamList[i]->mControl->bursts_per_sec(); ipg = 0; break; case OstProto::StreamControl::e_su_packets: numBursts = 1; - numPackets = streamList[i]->mControl.num_packets(); + numPackets = streamList[i]->mControl->num_packets(); ibg = 0; - ipg = 1000000/streamList[i]->mControl.packets_per_sec(); + ipg = 1000000/streamList[i]->mControl->packets_per_sec(); break; default: qWarning("Unhandled stream control unit %d", - streamList[i]->mControl.unit()); + streamList[i]->mControl->unit()); continue; } qDebug("numBursts = %ld, numPackets = %ld\n", @@ -773,7 +672,7 @@ void PortInfo::update() } } // for (numBursts) - switch(streamList[i]->mControl.next()) + switch(streamList[i]->mControl->next()) { case ::OstProto::StreamControl::e_nw_stop: goto _stop_no_more_pkts; @@ -798,7 +697,7 @@ void PortInfo::update() default: qFatal("---------- %s: Unhandled case (%d) -----------", - __FUNCTION__, streamList[i]->mControl.next() ); + __FUNCTION__, streamList[i]->mControl->next() ); break; } @@ -1378,18 +1277,7 @@ const ::OstProto::StreamIdList* request, s = response->add_stream(); - s->mutable_stream_id()->CopyFrom( - portInfo[portIdx]->streamList[streamIndex]->mStreamId); - s->mutable_core()->CopyFrom( - portInfo[portIdx]->streamList[streamIndex]->mCore); - s->mutable_control()->CopyFrom( - portInfo[portIdx]->streamList[streamIndex]->mControl); - for (int j=0; j < portInfo[portIdx]->streamList[streamIndex]-> - mProtocolList.size(); j++) - { - portInfo[portIdx]->streamList[streamIndex]-> - mProtocolList[j]->protoDataCopyInto(*s); - } + portInfo[portIdx]->streamList[streamIndex]->protoDataCopyInto(*s); } _exit: @@ -1494,17 +1382,8 @@ const ::OstProto::StreamConfigList* request, if (streamIndex < 0) continue; // TODO(LOW): Partial status of RPC - portInfo[portIdx]->streamList[streamIndex]->mCore.clear_frame_proto(); - portInfo[portIdx]->streamList[streamIndex]->mCore.MergeFrom( - request->stream(i).core()); - portInfo[portIdx]->streamList[streamIndex]->mControl.MergeFrom( - request->stream(i).control()); - for (int j=0; j < portInfo[portIdx]->streamList[streamIndex]-> - mProtocolList.size(); j++) - { - portInfo[portIdx]->streamList[streamIndex]-> - mProtocolList[j]->protoDataCopyFrom(request->stream(i)); - } + portInfo[portIdx]->streamList[streamIndex]->protoDataCopyFrom( + request->stream(i)); // TODO(LOW): fill-in response "Ack"???? } diff --git a/server/myservice.h b/server/myservice.h index 6a662fd..7a4f331 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -8,7 +8,7 @@ #endif #include "../common/protocol.pb.h" -#include "../common/abstractprotocol.h" +#include "../common/streambase.h" #include "abstracthost.h" #include #include @@ -30,31 +30,19 @@ class MyService; -class StreamInfo +class StreamInfo : public StreamBase { friend class MyService; friend class PortInfo; OstProto::StreamId mStreamId; - OstProto::StreamCore mCore; - OstProto::StreamControl mControl; - QList mProtocolList; public: StreamInfo(); ~StreamInfo(); private: - AbstractProtocol* protocolById(int id); - - quint32 pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, - quint8 protocol, quint16 len); - quint32 ipv4CksumPartial(uchar *buf, int len); - quint16 ipv4Cksum(uchar *buf, int len, quint32 partialSum = 0); int makePacket(uchar *buf, int bufMaxSize, int n); -public: - bool operator < (const StreamInfo &s) const - { return(mCore.ordinal() < s.mCore.ordinal()); } }; From 010369601604bdb7d921eabcb3cca6a9b8e26e22 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 24 May 2009 14:54:11 +0000 Subject: [PATCH 022/294] - AbstractProtocol - new method: fieldFlags() - isCksum, isMeta; Note: isMeta is a flag now not a attrib - fieldData() bit fields are now in lsb not msb - protocolFrameValue() and subclasses changed accordingly - protocolFrameValue() now takes an additional bool param indicating whether the frameValue is being requested for a checksum calculation; if so fields which are checksum fields are assumed to be zero and their value is not fetched to prevent infinite recursion - Other Protocols - mac: srcMac/dstMac modes is now working - vlan: implemented VLAN protocol - ip4: src/dst Addr modes is now working - udp/tcp: checksum done - Basic testing done for MAC, VLAN, IPv4, UDP and TCP protocols - sample protocol: .cpp/.h added to repos - need to be made compilable - StreamConfigDialog - Redesigned the protocol selection tab to accomodate "Advanced Protocol Selection" - L2 Tab config widgets are now in 2 columns - Packet Tree View is no longer collapsed if selected protocols don't change --- client/packetmodel.cpp | 7 +- client/portstatswindow.ui | 2 +- client/streamconfigdialog.cpp | 119 +++++-- client/streamconfigdialog.h | 6 +- client/streamconfigdialog.ui | 568 ++++++++++++++++++++++------------ common/abstractprotocol.cpp | 208 +++++++++---- common/abstractprotocol.h | 37 ++- common/dot3.ui | 4 +- common/ip4.cpp | 230 +++++++++++--- common/ip4.h | 7 +- common/ip4.ui | 6 +- common/mac.cpp | 98 ++++-- common/mac.h | 1 + common/mac.ui | 100 +++--- common/ostproto.pro | 4 + common/payload.cpp | 35 ++- common/payload.h | 3 + common/protocolmanager.cpp | 2 + common/protocolmanager.h | 1 + common/sample.cpp | 170 ++++++++++ common/sample.h | 56 ++++ common/snap.ui | 34 +- common/tcp.cpp | 105 +++++-- common/tcp.h | 13 +- common/udp.cpp | 73 ++++- common/udp.h | 1 + common/vlan.cpp | 229 ++++++++++++++ common/vlan.h | 62 ++++ common/vlan.proto | 8 +- common/vlan.ui | 311 ++++++++++--------- 30 files changed, 1862 insertions(+), 638 deletions(-) create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index f48f713..4dc4a31 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -10,8 +10,11 @@ PacketModel::PacketModel(const QList &selectedProtocols, void PacketModel::setSelectedProtocols( const QList &selectedProtocols) { - mSelectedProtocols = selectedProtocols; - reset(); + if (mSelectedProtocols != selectedProtocols) + { + mSelectedProtocols = selectedProtocols; + reset(); + } } int PacketModel::rowCount(const QModelIndex &parent) const diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui index 07fe1b2..8c6aed4 100644 --- a/client/portstatswindow.ui +++ b/client/portstatswindow.ui @@ -16,7 +16,7 @@ - QFrame::StyledPanel + QFrame::Panel QFrame::Raised diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index b69ef67..eb6f76c 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -19,14 +19,24 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, setupUi(this); setupUiExtra(); - connect(bgFrameType, SIGNAL(buttonClicked(int)), - this, SLOT(updateSelectedProtocols())); + connect(bgFrameType, SIGNAL(buttonClicked(int)), + this, SLOT(updateContents())); connect(bgL3Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateSelectedProtocols())); + this, SLOT(updateContents())); connect(bgL4Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateSelectedProtocols())); + this, SLOT(updateContents())); + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + // Time to play match the signals and slots! + + // Enable VLAN Choices only if FT = Eth2 or SNAP + connect(rbFtNone, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); // Enable/Disable L3 Protocol Choices for FT None @@ -96,8 +106,11 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); // TODO(MED): - //! \todo Implement then enable these protocols + + //! \todo Implement then enable these protocols - SVLAN, ARP, ICMP, IGMP + cbSVlan->setHidden(true); rbL3Arp->setHidden(true); + rbL3Ipv6->setHidden(true); rbL4Icmp->setHidden(true); rbL4Igmp->setHidden(true); //! \todo Enable navigation of streams @@ -132,6 +145,8 @@ void StreamConfigDialog::setupUiExtra() } // ---- Setup default stuff that cannot be done in designer ---- + gbVlan->setDisabled(true); + bgFrameType = new QButtonGroup(); foreach(QRadioButton *btn, gbFrameType->findChildren()) bgFrameType->addButton(btn); @@ -151,7 +166,8 @@ void StreamConfigDialog::setupUiExtra() ** Setup Validators */ // Meta Data - lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + //! \todo - doesn't seem to work - range validator needs a spinbox? + //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); // L2 Ethernet #if 0 // Proto FW @@ -228,6 +244,9 @@ void StreamConfigDialog::updateSelectedProtocols() // Mac mSelectedProtocols.append(51); + if (cbCVlan->isEnabled() && cbCVlan->isChecked()) + mSelectedProtocols.append(126); + if (rbFtEthernet2->isChecked()) mSelectedProtocols.append(121); else if (rbFt802Dot3Raw->isChecked()) @@ -260,12 +279,13 @@ void StreamConfigDialog::updateSelectedProtocols() // Payload mSelectedProtocols.append(52); - - mpStream->setFrameProtocol(mSelectedProtocols); - mpStream->storeProtocolWidgets(); - mpStream->loadProtocolWidgets(); } +void StreamConfigDialog::updateContents() +{ + StoreCurrentStream(); + LoadCurrentStream(); +} void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { @@ -331,13 +351,12 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) if (index != 2) return; - updateSelectedProtocols(); + updateContents(); foreach(int i, mSelectedProtocols) if (mpStream->protocol(i)) protoList.append(mpStream->protocol(i)); mpPacketModel->setSelectedProtocols(protoList); - StoreCurrentStream(mpStream); } void StreamConfigDialog::on_twProto_currentChanged(int index) @@ -368,6 +387,8 @@ void StreamConfigDialog::on_twProto_currentChanged(int index) { case 1: // L2 wl.append(mpStream->protocol("mac")->configWidget()); + if (cbCVlan->isEnabled() && cbCVlan->isChecked()) + wl.append(mpStream->protocol("vlan")->configWidget()); if (rbFtEthernet2->isChecked()) wl.append(mpStream->protocol("eth2")->configWidget()); else if (rbFt802Dot3Raw->isChecked()) @@ -383,28 +404,63 @@ void StreamConfigDialog::on_twProto_currentChanged(int index) wl.append(mpStream->protocol("llc")->configWidget()); wl.append(mpStream->protocol("snap")->configWidget()); } + + { + int i, r = 0, c = 0; + QGridLayout *layout = new QGridLayout; + + Q_ASSERT(wl.size() > 0); + + // We use a 2 column layout for the L2 Tab + + layout->addWidget(wl.at(0), r, c, 1, -1); + r++; + for (i=1; i < wl.size(); i++) + { + layout->addWidget(wl.at(i), r, c); + if ((i % 2) == 0) + r++; + c = (c+1) % 2; + + } + if ((i % 2) == 0) + r++; + layout->setRowStretch(r, 10); + + twProto->widget(index)->setLayout(layout); + } break; case 2: // L3 if (rbL3Ipv4->isChecked()) wl.append(mpStream->protocol("ip4")->configWidget()); + + if (wl.size()) + { + QVBoxLayout *layout = new QVBoxLayout; + + for (int i=0; i < wl.size(); i++) + layout->addWidget(wl.at(i)); + + twProto->widget(index)->setLayout(layout); + } break; case 3: // L4 if (rbL4Tcp->isChecked()) wl.append(mpStream->protocol("tcp")->configWidget()); else if (rbL4Udp->isChecked()) wl.append(mpStream->protocol("udp")->configWidget()); + + if (wl.size()) + { + QVBoxLayout *layout = new QVBoxLayout; + + for (int i=0; i < wl.size(); i++) + layout->addWidget(wl.at(i)); + + twProto->widget(index)->setLayout(layout); + } break; } - - if (wl.size()) - { - layout = new QVBoxLayout; - - for (int i=0; i < wl.size(); i++) - layout->addWidget(wl.at(i)); - - twProto->widget(index)->setLayout(layout); - } } void StreamConfigDialog::update_NumPacketsAndNumBursts() @@ -464,6 +520,13 @@ void StreamConfigDialog::LoadCurrentStream() Q_ASSERT(mSelectedProtocols.at(i) == 51); // Mac i++; + // VLAN + if (mSelectedProtocols.at(i) == 126) // VLAN + { + cbCVlan->setChecked(true); + i++; + } + if (mSelectedProtocols.at(i) == 52) // Payload { i++; @@ -500,6 +563,7 @@ void StreamConfigDialog::LoadCurrentStream() else rbFtNone->setChecked(true); + // L3 if (mSelectedProtocols.at(i) == 52) // Payload { @@ -611,10 +675,11 @@ _proto_parse_done: } } -void StreamConfigDialog::StoreCurrentStream(Stream *pStream) +void StreamConfigDialog::StoreCurrentStream() { QString str; bool isOk; + Stream *pStream = mpStream; qDebug("storing pStream %p", pStream); @@ -660,9 +725,15 @@ void StreamConfigDialog::StoreCurrentStream(Stream *pStream) void StreamConfigDialog::on_pbOk_clicked() { + OstProto::Stream s; + // Store dialog contents into stream - StoreCurrentStream(mPort.streamByIndex(mCurrentStreamIndex)); + //updateSelectedProtocols(); + StoreCurrentStream(); + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s);; qDebug("stream stored"); + lastTopLevelTabIndex = twTopLevel->currentIndex(); lastProtoTabIndex = twProto->currentIndex(); } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 75cae99..fa4aa82 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -49,15 +49,17 @@ private: static int lastProtoTabIndex; void setupUiExtra(); + void updateSelectedProtocols(); void LoadCurrentStream(); - void StoreCurrentStream(Stream *pStream); + void StoreCurrentStream(); private slots: void on_cmbPktLenMode_currentIndexChanged(QString mode); void on_pbPrev_clicked(); void on_pbNext_clicked(); - void updateSelectedProtocols(); + void updateContents(); + void on_twTopLevel_currentChanged(int index); void on_twProto_currentChanged(int index); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 9eba69a..d803e44 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,8 +8,8 @@ 0 0 - 590 - 517 + 636 + 589 @@ -29,8 +29,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff true - - + + @@ -98,11 +98,14 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff false + + 0099; + - 64 + - 32767 + 4 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -111,8 +114,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + - 64 + 32767 @@ -134,11 +140,14 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff false + + 0099; + - 64 + - 32767 + 4 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -157,187 +166,337 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Protocols - - - - - Frame Type - - - - - - None - - - true - - - - - - - Ethernet II - - - false - - - - - - - 802.3 Raw - - - - - - - 802.3 LLC - - - false - - - - - - - LLC SNAP - - - - - + + + + + + + Frame Type + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + LLC SNAP + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + true + + + CVLAN + + + + + + + true + + + SVLAN + + + + + + + + + + L3 + + + + + + None + + + true + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + ARP + + + + + + + false + + + Other + + + + + + + + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + + + + Qt::Vertical + + + + 182 + 16 + + + + + - - + + - L3 + Advanced Protocol Selection + + + true + + + false - - - None - - - true - - - - - - - false - - - IPv4 - - - false - - - - - - - false - - - ARP - - + + + + + + + Available Protocols + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + + + + + < + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + - - - - L4 - - - - - - None - - - true - - - - - - - false - - - ICMP - - - - - - - false - - - IGMP - - - - - - - false - - - TCP - - - - - - - false - - - UDP - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -410,8 +569,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + - 1 + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -743,7 +905,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + @@ -820,8 +982,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff accept() - 440 - 466 + 496 + 552 533 @@ -836,8 +998,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff reject() - 523 - 466 + 579 + 552 533 @@ -852,12 +1014,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 281 - 137 + 158 + 149 - 281 - 169 + 158 + 149 @@ -868,12 +1030,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 66 + 59 + 120 - 30 - 266 + 59 + 149 @@ -884,12 +1046,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 91 + 59 + 123 - 30 - 312 + 59 + 149 @@ -900,12 +1062,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 30 - 91 + 59 + 123 - 134 - 177 + 145 + 149 diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 04d0d67..b48f5b1 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -92,7 +92,7 @@ int AbstractProtocol::fieldCount() const /*! Returns the number of meta fields \n The default implementation counts and returns the number of fields for which - fieldData(index, FieldIsMeta) return true\n + the FieldIsMeta flag is set\n The default implementation caches the count on its first invocation and subsequently returns the cached count */ int AbstractProtocol::metaFieldCount() const @@ -101,7 +101,7 @@ int AbstractProtocol::metaFieldCount() const { int c = 0; for (int i = 0; i < fieldCount() ; i++) - if (fieldData(i, FieldIsMeta).toBool()) + if (fieldFlags(i).testFlag(FieldIsMeta)) c++; metaCount = c; } @@ -117,6 +117,11 @@ int AbstractProtocol::frameFieldCount() const return (fieldCount() - metaFieldCount()); } +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const +{ + return FieldIsNormal; +} + /*! Returns the requested field attribute data \n Protocols which have meta fields that vary a frame field across streams may use the streamIndex to return the appropriate field value \n @@ -140,6 +145,9 @@ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString(); case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); return fieldData(index, FieldFrameValue, streamIndex). toByteArray().size() * 8; case FieldValue: @@ -148,8 +156,6 @@ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, return QByteArray(); case FieldTextValue: return QString(); - case FieldIsMeta: - return false; default: qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, @@ -196,7 +202,7 @@ int AbstractProtocol::protocolFrameSize() const for (int i = 0; i < fieldCount(); i++) { - if (!fieldData(i, FieldIsMeta).toBool()) + if (!fieldFlags(i).testFlag(FieldIsMeta)) bitsize += fieldData(i, FieldBitSize).toUInt(); } protoSize = (bitsize+7)/8; @@ -248,23 +254,30 @@ int AbstractProtocol::protocolFramePayloadSize() const The default implementation forms and returns an ordered concatenation of the FrameValue of all the 'frame' fields of the protocol taking care of fields which are not an integral number of bytes\n */ -QByteArray AbstractProtocol::protocolFrameValue(int streamIndex) const +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const { QByteArray proto, field; - int bits, lastbitpos = 0; + uint bits, lastbitpos = 0; + FieldFlags flags; for (int i=0; i < fieldCount() ; i++) { - if (!fieldData(i, FieldIsMeta).toBool()) + flags = fieldFlags(i); + if (!flags.testFlag(FieldIsMeta)) { - field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); - if (bits == 0) - continue; + Q_ASSERT(bits > 0); - qDebug("<<< %d, %d >>>>", proto.size(), field.size()); + if (forCksum && flags.testFlag(FieldIsCksum)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size()); - if (bits == field.size() * 8) + if (bits == (uint) field.size() * 8) { if (lastbitpos == 0) proto.append(field); @@ -279,28 +292,44 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex) const field.at(j+1) >> lastbitpos); } } - else if (bits < field.size() * 8) + else if (bits < (uint) field.size() * 8) { - int u, v; + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); - u = bits / 8; - v = bits % 8; if (lastbitpos == 0) { - proto.append(field.left(u+1)); - char c = proto[proto.size() - 1]; - proto[proto.size() - 1] = c & (0xFF << (8 - v)); - lastbitpos = v; + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; } else { - char c = proto[proto.size() - 1]; - proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); - for (int j = 0; j < (u - 1); j++) - proto.append(field.at(j) << lastbitpos | - field.at(j+1) >> lastbitpos); - if (u) - proto.append( field.at(u) & (0xFF << (8 - v)) ); + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + lastbitpos = (lastbitpos + bits) % 8; } } @@ -315,53 +344,124 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex) const return proto; } -QVariant AbstractProtocol::protocolFrameCksum() const +/*! + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion + */ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const { - QByteArray fv; - quint16 *ip; - quint32 len, sum = 0; + static int recursionCount = 0; + quint32 cksum = 0; - fv = protocolFrameValue(-1); - ip = (quint16*) fv.constData(); - len = fv.size(); + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); - while(len > 1) + switch(cksumType) { - sum += *ip; - if(sum & 0x80000000) - sum = (sum & 0xFFFF) + (sum >> 16); - ip++; - len -= 2; + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; } - if (len) - sum += (unsigned short) *(unsigned char *)ip; + recursionCount--; + return cksum; +} + +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType) const +{ + quint32 sum = 0xFFFF; + QLinkedListIterator iter(frameProtocols); + + Q_ASSERT(cksumType == CksumIpPseudo); + + if (iter.findNext(this)) + { + iter.previous(); + if (iter.hasPrevious()) + sum = iter.previous()->protocolFrameCksum(streamIndex, + CksumIpPseudo); + } while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); - return qFromBigEndian((quint16) ~sum); + return (quint16) ~sum; } -QVariant AbstractProtocol::protocolFramePayloadCksum() const +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType) const { - int cksum = 0; + quint32 sum = 0; + quint16 cksum; QLinkedListIterator iter(frameProtocols); + Q_ASSERT(cksumType == CksumIp); + if (iter.findNext(this)) { while (iter.hasNext()) - cksum += iter.next()->protocolFrameCksum().toUInt(); // TODO: chg to partial + { + cksum = iter.next()->protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cksum; + } } else - return -1; -#if 0 - while(cksum>>16) - cksum = (cksum & 0xFFFF) + (cksum >> 16); + return 0; - return (quint16) ~cksum; -#endif + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); - return cksum; + return (quint16) ~sum; } diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index ec0584b..bbe8325 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -6,6 +6,7 @@ #include #include #include +#include //#include "../rpc/pbhelper.h" #include "../common/protocol.pb.h" @@ -35,13 +36,19 @@ protected: ProtocolList &frameProtocols; public: + enum FieldFlag { + FieldIsNormal = 0x0, + FieldIsMeta = 0x1, + FieldIsCksum = 0x2 + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); + enum FieldAttrib { FieldName, //! name FieldValue, //! value in host byte order (user editable) FieldTextValue, //! value as text FieldFrameValue, //! frame encoded value in network byte order FieldBitSize, //! size in bits - FieldIsMeta //! bool indicating if field is meta }; enum ProtocolIdType { @@ -50,6 +57,14 @@ public: ProtocolIdIp, }; + enum CksumType { + CksumIp, + CksumIpPseudo, + CksumTcpUdp, + + CksumMax + }; + AbstractProtocol(ProtocolList &frameProtoList, OstProto::StreamCore *parent = 0); virtual ~AbstractProtocol(); @@ -71,22 +86,30 @@ public: virtual int metaFieldCount() const; int frameFieldCount() const; + virtual FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; + int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + FieldAttrib attrib = FieldValue); - QByteArray protocolFrameValue(int streamIndex = 0) const; - int protocolFrameSize() const; + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; int protocolFrameOffset() const; int protocolFramePayloadSize() const; - virtual QVariant protocolFrameCksum() const; - QVariant protocolFramePayloadCksum() const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; virtual QWidget* configWidget() = 0; virtual void loadConfigWidget() = 0; virtual void storeConfigWidget() = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + #endif diff --git a/common/dot3.ui b/common/dot3.ui index d452bd0..7473ee2 100644 --- a/common/dot3.ui +++ b/common/dot3.ui @@ -46,8 +46,8 @@ - 40 - 20 + 16 + 54 diff --git a/common/ip4.cpp b/common/ip4.cpp index 260c720..59a6773 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -10,10 +10,12 @@ Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) { setupUi(this); + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); } void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) @@ -110,45 +112,104 @@ int Ip4Protocol::fieldCount() const return ip4_fieldCount; } +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= FieldIsCksum; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags |= FieldIsMeta; + break; + + default: + break; + } + + return flags; +} + QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + switch(attrib) { case FieldName: return QString("Version"); case FieldValue: - return (data.ver_hdrlen() >> 4) & 0x0F; + return ver; case FieldTextValue: - return QString("%1").arg((data.ver_hdrlen() >> 4) & 0x0F); + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); case FieldFrameValue: - return QByteArray(1, (char)(data.ver_hdrlen() & 0xF0)); + return QByteArray(1, (char) ver); case FieldBitSize: return 4; default: break; } break; + } case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + switch(attrib) { case FieldName: return QString("Header Length"); case FieldValue: - return data.ver_hdrlen() & 0x0F; + return hdrlen; case FieldTextValue: - return QString("%1").arg(data.ver_hdrlen() & 0x0F); + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); case FieldFrameValue: - return QByteArray(1, (char)(data.ver_hdrlen() << 4)); + return QByteArray(1, (char) hdrlen); case FieldBitSize: return 4; default: break; } break; + } case ip4_tos: switch(attrib) { @@ -261,11 +322,12 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; // FIXME need to shift for 13 bits fv.resize(2); - qToBigEndian((quint16) data.frag_ofs(), (uchar*) fv.data()); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); return fv; } case FieldTextValue: - return QString("%1").arg(data.frag_ofs()); + return QString("%1").arg(data.frag_ofs()*8); case FieldBitSize: return 13; default: @@ -316,7 +378,6 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, } case ip4_cksum: { - switch(attrib) { case FieldName: @@ -325,12 +386,10 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, { quint16 cksum; - if (streamIndex < 0) - cksum = 0; - else if (data.is_override_cksum()) + if (data.is_override_cksum()) cksum = data.cksum(); else - cksum = protocolFrameCksum().toUInt(); + cksum = protocolFrameCksum(streamIndex, CksumIp); return cksum; } case FieldFrameValue: @@ -338,12 +397,11 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; quint16 cksum; - if (streamIndex < 0) - cksum = 0; - else if (data.is_override_cksum()) + if (data.is_override_cksum()) cksum = data.cksum(); else - cksum = protocolFrameCksum().toUInt(); + cksum = protocolFrameCksum(streamIndex, CksumIp); + fv.resize(2); qToBigEndian((quint16) cksum, (uchar*) fv.data()); return fv; @@ -352,12 +410,10 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, { quint16 cksum; - if (streamIndex < 0) - cksum = 0; - else if (data.is_override_cksum()) + if (data.is_override_cksum()) cksum = data.cksum(); else - cksum = protocolFrameCksum().toUInt(); + cksum = protocolFrameCksum(streamIndex, CksumIp); return QString("0x%1"). arg(cksum, 4, BASE_HEX, QChar('0'));; } @@ -369,46 +425,111 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, break; } case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + switch(attrib) { case FieldName: return QString("Source"); case FieldValue: - return data.src_ip(); + return srcIp; case FieldFrameValue: { QByteArray fv; fv.resize(4); - qToBigEndian((quint32) data.src_ip(), (uchar*) fv.data()); + qToBigEndian(srcIp, (uchar*) fv.data()); return fv; } case FieldTextValue: - return QHostAddress(data.src_ip()).toString(); + return QHostAddress(srcIp).toString(); default: break; } break; + } case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + switch(attrib) { case FieldName: return QString("Destination"); case FieldValue: - return data.dst_ip(); + return dstIp; case FieldFrameValue: { QByteArray fv; fv.resize(4); - qToBigEndian((quint32) data.dst_ip(), (uchar*) fv.data()); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); return fv; } case FieldTextValue: - return QHostAddress(data.dst_ip()).toString(); + return QHostAddress(dstIp).toString(); default: break; } break; - + } // Meta fields case ip4_isOverrideVer: @@ -423,15 +544,6 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, case ip4_dstAddrMode: case ip4_dstAddrCount: case ip4_dstAddrMask: - switch(attrib) - { - case FieldIsMeta: - return true; - default: - break; - } - break; - default: break; } @@ -461,6 +573,34 @@ bool Ip4Protocol::setFieldData(int index, const QVariant &value, return isOk; } +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} QWidget* Ip4Protocol::configWidget() { @@ -469,15 +609,16 @@ QWidget* Ip4Protocol::configWidget() void Ip4Protocol::loadConfigWidget() { - configForm->leIpVersion->setText(QString().setNum(data.ver_hdrlen() >> 4)); configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); - configForm->leIpHdrLen->setText(QString().setNum(data.ver_hdrlen() & 0x0F)); configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); - configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); configForm->leIpId->setText(uintToHexStr(data.id(), 2)); configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); @@ -488,9 +629,9 @@ void Ip4Protocol::loadConfigWidget() configForm->leIpProto->setText(uintToHexStr( fieldData(ip4_proto, FieldValue).toUInt(), 1)); + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); configForm->leIpCksum->setText(uintToHexStr( fieldData(ip4_cksum, FieldValue).toUInt(), 2)); - configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); @@ -528,8 +669,8 @@ void Ip4Protocol::storeConfigWidget() data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); - data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); @@ -539,5 +680,6 @@ void Ip4Protocol::storeConfigWidget() data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); } diff --git a/common/ip4.h b/common/ip4.h index 1bf8ccc..9c20936 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -6,9 +6,9 @@ #include "ip4.pb.h" #include "ui_ip4.h" -#define IP_FLAG_UNUSED 0x1 +#define IP_FLAG_MF 0x1 #define IP_FLAG_DF 0x2 -#define IP_FLAG_MF 0x4 +#define IP_FLAG_UNUSED 0x4 class Ip4ConfigForm : public QWidget, public Ui::ip4 @@ -74,10 +74,13 @@ public: virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/ip4.ui b/common/ip4.ui index 785e7e3..a3135e7 100644 --- a/common/ip4.ui +++ b/common/ip4.ui @@ -5,7 +5,7 @@ 0 0 - 504 + 527 296 @@ -35,7 +35,7 @@ - Override Header Length + Override Header Length (x4) @@ -118,7 +118,7 @@ - Fragment Offset + Fragment Offset (x8) diff --git a/common/mac.cpp b/common/mac.cpp index 53defa7..1adc9ca 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -94,26 +94,73 @@ int MacProtocol::fieldCount() const return mac_fieldCount; } +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags |= FieldIsMeta; + break; + } + + return flags; +} + QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + switch(attrib) { case FieldName: return QString("Desination"); case FieldValue: - return data.dst_mac(); + return dstMac; case FieldTextValue: - return QString("%1").arg(data.dst_mac(), 12, BASE_HEX, - QChar('0')); + return uintToHexStr(dstMac, 6); case FieldFrameValue: { QByteArray fv; fv.resize(8); - qToBigEndian((quint64) data.dst_mac(), (uchar*) fv.data()); + qToBigEndian(dstMac, (uchar*) fv.data()); fv.remove(0, 2); return fv; } @@ -121,22 +168,44 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, break; } break; - + } case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + switch(attrib) { case FieldName: return QString("Source"); case FieldValue: - return data.src_mac(); + return srcMac; case FieldTextValue: - return QString("%1").arg(data.src_mac(), 12, BASE_HEX, - QChar('0')); + return uintToHexStr(srcMac, 6); case FieldFrameValue: { QByteArray fv; fv.resize(8); - qToBigEndian((quint64) data.src_mac(), (uchar*) fv.data()); + qToBigEndian(srcMac, (uchar*) fv.data()); fv.remove(0, 2); return fv; } @@ -144,7 +213,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, break; } break; - + } // Meta fields case mac_dstMacMode: case mac_dstMacCount: @@ -152,15 +221,6 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, case mac_srcMacMode: case mac_srcMacCount: case mac_srcMacStep: - switch(attrib) - { - case FieldIsMeta: - return true; - default: - break; - } - break; - default: break; } diff --git a/common/mac.h b/common/mac.h index 575b9b1..84199a5 100644 --- a/common/mac.h +++ b/common/mac.h @@ -55,6 +55,7 @@ public: virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/mac.ui b/common/mac.ui index 9afb006..ca1350e 100644 --- a/common/mac.ui +++ b/common/mac.ui @@ -5,8 +5,8 @@ 0 0 - 423 - 144 + 512 + 98 @@ -19,28 +19,14 @@ MAC - - - - Mode - - - - - - - Step - - - - + Destination - + @@ -56,7 +42,14 @@ - + + + + Mode + + + + @@ -75,7 +68,14 @@ - + + + + Count + + + + false @@ -88,7 +88,14 @@ - + + + + Step + + + + false @@ -101,14 +108,14 @@ - + Source - + >HH HH HH HH HH HH; @@ -118,7 +125,14 @@ - + + + + Mode + + + + @@ -137,7 +151,14 @@ - + + + + Count + + + + false @@ -147,7 +168,14 @@ - + + + + Step + + + + false @@ -160,29 +188,9 @@ - - - - Count - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - diff --git a/common/ostproto.pro b/common/ostproto.pro index 59b2453..192049f 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -10,6 +10,7 @@ FORMS += \ dot3.ui \ llc.ui \ snap.ui \ + vlan.ui \ ip4.ui \ tcp.ui \ udp.ui @@ -21,6 +22,7 @@ PROTOS += \ dot3.proto \ llc.proto \ snap.proto \ + vlan.proto \ ip4.proto \ tcp.proto \ udp.proto @@ -35,6 +37,7 @@ HEADERS += \ dot3.h \ llc.h \ snap.h \ + vlan.h \ ip4.h \ tcp.h \ udp.h @@ -49,6 +52,7 @@ SOURCES += \ dot3.cpp \ llc.cpp \ snap.cpp \ + vlan.cpp \ ip4.cpp \ tcp.cpp \ udp.cpp diff --git a/common/payload.cpp b/common/payload.cpp index 8e79c7d..ecaef9b 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -74,11 +74,36 @@ QString PayloadProtocol::shortName() const return QString("DATA"); } +int PayloadProtocol::protocolFrameSize() const +{ + return (stream->frame_len() - protocolFrameOffset() - SZ_FCS); +} + int PayloadProtocol::fieldCount() const { return payload_fieldCount; } +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags |= FieldIsMeta; + break; + } + + return flags; +} + QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -118,6 +143,7 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, fv[i] = 0xFF - (i % (0xFF + 1)); break; case OstProto::Payload::e_dp_random: + //! \todo cksum will be incorrect for random pattern for (int i = 0; i < dataLen; i++) fv[i] = qrand() % (0xFF + 1); break; @@ -136,15 +162,6 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, // Meta fields case payload_dataPatternMode: - switch(attrib) - { - case FieldIsMeta: - return true; - default: - break; - } - break; - default: break; } diff --git a/common/payload.h b/common/payload.h index 4a1dc03..34e9a01 100644 --- a/common/payload.h +++ b/common/payload.h @@ -45,8 +45,11 @@ public: virtual QString name() const; virtual QString shortName() const; + virtual int protocolFrameSize() const; + virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 7b49e33..9041adc 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -7,6 +7,7 @@ #include "dot3.h" #include "llc.h" #include "snap.h" +#include "vlan.h" #include "ip4.h" #include "tcp.h" #include "udp.h" @@ -24,6 +25,7 @@ ProtocolManager::ProtocolManager() registerProtocol(122, QString("dot3"), (void*) Dot3Protocol::createInstance); registerProtocol(123, QString("llc"), (void*) LlcProtocol::createInstance); registerProtocol(124, QString("snap"), (void*) SnapProtocol::createInstance); + registerProtocol(126, QString("vlan"), (void*) VlanProtocol::createInstance); registerProtocol(130, QString("ip4"), (void*) Ip4Protocol::createInstance); registerProtocol(140, QString("tcp"), (void*) TcpProtocol::createInstance); registerProtocol(141, QString("udp"), (void*) UdpProtocol::createInstance); diff --git a/common/protocolmanager.h b/common/protocolmanager.h index 5d8e1ca..de602fc 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -6,6 +6,7 @@ class ProtocolManager { public: + //! \todo Make these data structures private/protected static QMap nameToNumberMap; static QMap factory; diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..e92c9fc --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,170 @@ +#include +#include + +#include "sample.h" + +SampleConfigForm *SampleProtocol::configForm = NULL; + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + +} + +SampleProtocol::SampleProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) +{ + if (configForm == NULL) + configForm = new SampleConfigForm; +} + +SampleProtocol::~SampleProtocol() +{ +} + +AbstractProtocol* SampleProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new SampleProtocol(frameProtoList, streamCore); +} + +void SampleProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::sample)->CopyFrom(data); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::sample)) + data.MergeFrom(stream.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample"); +} + +QString SampleProtocol::shortName() const +{ + return QString("Sample"); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_normal: + break; + + case sample_cksum: + flags |= FieldIsCksum; + break; + + case sample_meta: + flags |= FieldIsMeta; + break; + + default: + break; + } + + return flags; +} + +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_FIXME: + { + switch(attrib) + { + case FieldName: + return QString("FIXME"); + case FieldValue: + return data.FIXME(); + case FieldTextValue: + return QString("%1").arg(data.FIXME()); + case FieldFrameValue: + return QByteArray(1, (char)(data.FIXME() & 0xF0)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + } + case sample_FIXME: + { + switch(attrib) + { + case FieldName: + return QString("FIXME"); + case FieldValue: + return FIXME; + case FieldTextValue: + return QString("%1").arg(FIXME); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(0); + qToBigEndian(FIXME, (uchar*) fv.data()); + return fv; + } + return QByteArray(1, (char)(FIXME() & 0xF0)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + // Meta fields + + case sample_FIXME: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* SampleProtocol::configWidget() +{ + return configForm; +} + +void SampleProtocol::loadConfigWidget() +{ +} + +void SampleProtocol::storeConfigWidget() +{ + bool isOk; +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..9da187d --- /dev/null +++ b/common/sample.h @@ -0,0 +1,56 @@ +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "abstractprotocol.h" + +#include "sample.pb.h" +#include "ui_sample.h" + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + static SampleConfigForm *configForm; + enum samplefield + { + + sample_fieldCount + }; + +public: + SampleProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.ui b/common/snap.ui index 9d0e4a5..80997bd 100644 --- a/common/snap.ui +++ b/common/snap.ui @@ -5,15 +5,15 @@ 0 0 - 293 - 98 + 194 + 72 Form - - + + SNAP @@ -56,32 +56,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/common/tcp.cpp b/common/tcp.cpp index 26b14c0..ab13d54 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -70,6 +70,43 @@ int TcpProtocol::fieldCount() const return tcp_fieldCount; } +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= FieldIsCksum; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags |= FieldIsMeta; + break; + + default: + break; + } + + return flags; +} + QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -142,7 +179,7 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("Sequence Number"); + return QString("Acknowledgement Number"); case FieldValue: return data.ack_num(); case FieldTextValue: @@ -165,11 +202,22 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("Header Length"); case FieldValue: - return ((data.hdrlen_rsvd() >> 4) & 0x0F); + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; case FieldTextValue: - return QString("%1").arg((data.hdrlen_rsvd() >> 4) & 0x0F); + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); case FieldFrameValue: - return QByteArray(1, (char)(data.hdrlen_rsvd() & 0xF0)); + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); case FieldBitSize: return 4; default: @@ -253,16 +301,43 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("Checksum"); case FieldValue: - return data.cksum(); + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } case FieldTextValue: - return QString("%1").arg(data.cksum()); + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } case FieldFrameValue: { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + QByteArray fv; fv.resize(2); - qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + qToBigEndian(cksum, (uchar*) fv.data()); return fv; } + case FieldBitSize: + return 16; default: break; } @@ -292,15 +367,6 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, // Meta fields case tcp_is_override_hdrlen: case tcp_is_override_cksum: - switch(attrib) - { - case FieldIsMeta: - return true; - default: - break; - } - break; - default: break; } @@ -329,12 +395,13 @@ void TcpProtocol::loadConfigWidget() configForm->leTcpSeqNum->setText(QString().setNum(data.seq_num())); configForm->leTcpAckNum->setText(QString().setNum(data.ack_num())); - configForm->leTcpHdrLen->setText(QString().setNum((data.hdrlen_rsvd() >> 4) & 0x0F)); + configForm->leTcpHdrLen->setText(fieldData(tcp_hdrlen, FieldValue).toString()); configForm->cbTcpHdrLenOverride->setChecked(data.is_override_hdrlen()); configForm->leTcpWindow->setText(QString().setNum(data.window())); - configForm->leTcpCksum->setText(QString().setNum(data.cksum())); + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); configForm->cbTcpCksumOverride->setChecked(data.is_override_cksum()); configForm->leTcpUrgentPointer->setText(QString().setNum(data.urg_ptr())); @@ -363,7 +430,7 @@ void TcpProtocol::storeConfigWidget() data.set_window(configForm->leTcpWindow->text().toULong(&isOk)); - data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); data.set_is_override_cksum(configForm->cbTcpCksumOverride->isChecked()); data.set_urg_ptr(configForm->leTcpUrgentPointer->text().toULong(&isOk)); diff --git a/common/tcp.h b/common/tcp.h index 0637fc1..bfe165b 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -6,12 +6,12 @@ #include "tcp.pb.h" #include "ui_tcp.h" -#define TCP_FLAG_URG 0x01 -#define TCP_FLAG_ACK 0x02 -#define TCP_FLAG_PSH 0x04 -#define TCP_FLAG_RST 0x08 -#define TCP_FLAG_SYN 0x10 -#define TCP_FLAG_FIN 0x20 +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 class TcpConfigForm : public QWidget, public Ui::tcp { @@ -62,6 +62,7 @@ public: virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/udp.cpp b/common/udp.cpp index 4d8585a..4c642c2 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -70,6 +70,35 @@ int UdpProtocol::fieldCount() const return udp_fieldCount; } +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= FieldIsCksum; + break; + + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags |= FieldIsMeta; + break; + + default: + break; + } + + return flags; +} + QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -160,26 +189,49 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, break; } case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + } + default: + break; + } + switch(attrib) { case FieldName: return QString("Checksum"); case FieldValue: - return data.cksum(); - case FieldTextValue: - return QString("%1").arg(data.cksum()); + return cksum; case FieldFrameValue: { QByteArray fv; + fv.resize(2); - qToBigEndian((quint16) data.cksum(), (uchar*) fv.data()); + qToBigEndian(cksum, (uchar*) fv.data()); return fv; } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; default: break; } break; - + } // Meta fields case udp_isOverrideTotLen: case udp_isOverrideCksum: @@ -214,13 +266,14 @@ QWidget* UdpProtocol::configWidget() void UdpProtocol::loadConfigWidget() { - configForm->leUdpSrcPort->setText(QString().setNum(data.src_port())); - configForm->leUdpDstPort->setText(QString().setNum(data.dst_port())); + configForm->leUdpSrcPort->setText(fieldData(udp_srcPort, FieldValue).toString()); + configForm->leUdpDstPort->setText(fieldData(udp_dstPort, FieldValue).toString()); - configForm->leUdpLength->setText(QString().setNum(data.totlen())); + configForm->leUdpLength->setText(fieldData(udp_totLen, FieldValue).toString()); configForm->cbUdpLengthOverride->setChecked(data.is_override_totlen()); - configForm->leUdpCksum->setText(QString().setNum(data.cksum())); + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); configForm->cbUdpCksumOverride->setChecked(data.is_override_cksum()); } @@ -234,7 +287,7 @@ void UdpProtocol::storeConfigWidget() data.set_totlen(configForm->leUdpLength->text().toULong(&isOk)); data.set_is_override_totlen(configForm->cbUdpLengthOverride->isChecked()); - data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); data.set_is_override_cksum(configForm->cbUdpCksumOverride->isChecked()); } diff --git a/common/udp.h b/common/udp.h index 5c40330..0fea509 100644 --- a/common/udp.h +++ b/common/udp.h @@ -49,6 +49,7 @@ public: virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..7908e8e --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,229 @@ +#include + +#include "vlan.h" + +VlanConfigForm *VlanProtocol::configForm = NULL; + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol( + ProtocolList &frameProtoList, + OstProto::StreamCore *parent) + : AbstractProtocol(frameProtoList, parent) +{ + if (configForm == NULL) + configForm = new VlanConfigForm; +} + +VlanProtocol::~VlanProtocol() +{ +} + +AbstractProtocol* VlanProtocol::createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore) +{ + return new VlanProtocol(frameProtoList, streamCore); +} + +void VlanProtocol::protoDataCopyInto(OstProto::Stream &stream) +{ + // FIXME: multiple headers + stream.MutableExtension(OstProto::vlan)->CopyFrom(data); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +{ + // FIXME: multiple headers + if (stream.HasExtension(OstProto::vlan)) + data.MergeFrom(stream.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags |= FieldIsMeta; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + // FIXME + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); + +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..23edf44 --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,62 @@ +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + OstProto::Vlan data; + static VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +public: + VlanProtocol(ProtocolList &frameProtoList, + OstProto::StreamCore *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance( + ProtocolList &frameProtoList, + OstProto::StreamCore *streamCore = 0); + + virtual void protoDataCopyInto(OstProto::Stream &stream); + virtual void protoDataCopyFrom(const OstProto::Stream &stream); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto index d5ac773..6703dbc 100644 --- a/common/vlan.proto +++ b/common/vlan.proto @@ -3,13 +3,11 @@ import "protocol.proto"; package OstProto; message Vlan { // VLAN presence/absence - optional bool is_tpid_override = 10; + optional bool is_override_tpid = 1; // VLAN values - optional uint32 ctpid = 13; - optional uint32 cvlan_tag = 14; // includes prio, cfi and cvlanid - optional uint32 stpid = 15; - optional uint32 svlan_tag = 16; // includes pcp, de and svlanid + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid } extend Stream { diff --git a/common/vlan.ui b/common/vlan.ui index c35dc89..3e0326d 100644 --- a/common/vlan.ui +++ b/common/vlan.ui @@ -1,168 +1,179 @@ - Form - + Vlan + 0 0 - 271 - 90 + 268 + 96 Form - - - - - - - Priority - - - - - - - CFI - - - - - - - VLAN - - - - - - - true - - - Override TPID - - - - - - - true - - + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + 0 - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 - - - - - - - - true - - - - 0 - - - - - 1 - - - - - - - - true - - - 0 - - - - - - - true - - - >HH HH; - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + - + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + From 1357f495acad74221f64f002cfac7df8dbfc2ac7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 2 Aug 2009 14:52:34 +0000 Subject: [PATCH 023/294] Major rewrite of the protocol framework - changes not yet complete Common ------ - Change in OstProto - Individual protocols are now extensions of (new) message 'Protocol' instead of 'Stream' - Stream now contains a repeated Protocol which also determines the ordered set of currently selected protocols; StreamCore.frame_proto which was doing this earlier has been removed - Change in AbstractProtocol Interface - Parent changed to StreamBase - Corresponding change in constructor and factory func - createInstance() - new method protocolNumber() - returns unique id for each protocol - protoDataCopyInto/From() now copies into OstProto::Protocol instead of OstProto::Stream - Change in all subclasses of AbstractProtocol to match new interface - For all protocols, configFrom is no longer static, but each protocol has its own configForm - configForm creation is lazy - configForm is still a child of the protocol i.e. it will be destroyed alongwith the protocol - TODO: convert configWidget() to a pure factory function i.e. the protocol does not own the configForm - this requires us to pass the widget into load/storeConfigWidget() methods - ProtocolCollection class removed alongwith its .h and .cpp - ProtocolList class redefined to serve the purpose of ProtocolCollection - New class ProtocolListIterator defined to iterate ProtocolList - AbstractProtocol methods now use the ProtocolListIterator - Factory function createProtocol() added to ProtocolManager - OstProto::StreamCore accessor functions moved from Stream to StreamBase Server ------ - MyService uses the newly moved accessors to StreamBase for OstProto::StreamCore members - StreamInfo now uses the protocols to create the packet Client ------ - StreamConfigDialog now uses ProtocolListIterator - So does PacketModel --- client/packetmodel.cpp | 22 +- client/packetmodel.h | 12 +- client/stream.cpp | 47 +++- client/stream.h | 128 +-------- client/streamconfigdialog.cpp | 265 +++++++++++++++++-- client/streamconfigdialog.h | 9 +- client/streamconfigdialog.ui | 26 +- client/streammodel.cpp | 2 +- common/abstractprotocol.cpp | 70 ++--- common/abstractprotocol.h | 23 +- common/dot3.cpp | 52 ++-- common/dot3.h | 14 +- common/dot3.proto | 2 +- common/eth2.cpp | 43 ++-- common/eth2.h | 14 +- common/eth2.proto | 2 +- common/eth2.ui | 64 ++--- common/ip4.cpp | 53 ++-- common/ip4.h | 15 +- common/ip4.proto | 2 +- common/llc.cpp | 43 ++-- common/llc.h | 14 +- common/llc.proto | 2 +- common/mac.cpp | 48 ++-- common/mac.h | 15 +- common/mac.proto | 2 +- common/mac.ui | 31 ++- common/ostproto.pro | 6 +- common/payload.cpp | 49 ++-- common/payload.h | 14 +- common/payload.proto | 2 +- common/protocol.proto | 37 ++- common/protocolcollection.cpp | 106 -------- common/protocolcollection.h | 30 --- common/protocollist.cpp | 8 + common/protocollist.h | 9 + common/protocollistiterator.cpp | 87 +++++++ common/protocollistiterator.h | 29 +++ common/protocolmanager.cpp | 35 ++- common/protocolmanager.h | 17 +- common/sample.cpp | 42 +-- common/sample.h | 14 +- common/snap.cpp | 43 ++-- common/snap.h | 14 +- common/snap.proto | 2 +- common/streambase.cpp | 274 +++++++++++++++++--- common/streambase.h | 104 +++++++- common/tcp.cpp | 43 ++-- common/tcp.h | 14 +- common/tcp.proto | 2 +- common/udp.cpp | 43 ++-- common/udp.h | 14 +- common/udp.proto | 2 +- common/vlan.cpp | 44 ++-- common/vlan.h | 14 +- common/vlan.proto | 2 +- server/myservice.cpp | 442 ++------------------------------ 57 files changed, 1370 insertions(+), 1192 deletions(-) delete mode 100644 common/protocolcollection.cpp delete mode 100644 common/protocolcollection.h create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 4dc4a31..7d9df3d 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,18 +1,24 @@ #include -#include "packetmodel.h" -PacketModel::PacketModel(const QList &selectedProtocols, - QObject *parent) +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) { - mSelectedProtocols = selectedProtocols; } -void PacketModel::setSelectedProtocols( - const QList &selectedProtocols) +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) { - if (mSelectedProtocols != selectedProtocols) + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) { - mSelectedProtocols = selectedProtocols; + mSelectedProtocols = currentProtocols; reset(); } } diff --git a/client/packetmodel.h b/client/packetmodel.h index b268134..c6b0cfc 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -2,16 +2,16 @@ #define _PACKET_MODEL_H #include -#include "abstractprotocol.h" + +class ProtocolListIterator; +class AbstractProtocol; class PacketModel: public QAbstractItemModel { public: - PacketModel(const QList &selectedProtocols, - QObject *parent = 0); - void setSelectedProtocols( - const QList &selectedProtocols); + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; @@ -34,7 +34,7 @@ private: } ws; } IndexId; - QList mSelectedProtocols; + QList mSelectedProtocols; }; #endif diff --git a/client/stream.cpp b/client/stream.cpp index b978913..d2791e3 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -2,16 +2,14 @@ #include #include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" Stream::Stream() { //mId = 0xFFFFFFFF; - mCore->set_is_enabled(true); - - QList protoList; - protoList.append(51); - protoList.append(52); - setFrameProtocol(protoList); + setEnabled(true); } Stream::~Stream() @@ -20,10 +18,43 @@ Stream::~Stream() void Stream::loadProtocolWidgets() { - protocols.loadConfigWidgets(); +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif } void Stream::storeProtocolWidgets() { - protocols.storeConfigWidgets(); +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif } diff --git a/client/stream.h b/client/stream.h index 1c19b19..1539af3 100644 --- a/client/stream.h +++ b/client/stream.h @@ -13,135 +13,11 @@ class Stream : public StreamBase { //quint32 mId; public: - void loadProtocolWidgets(); - void storeProtocolWidgets(); - -public: - enum FrameLengthMode { - e_fl_fixed, - e_fl_inc, - e_fl_dec, - e_fl_random - }; - - enum SendUnit { - e_su_packets, - e_su_bursts - }; - - enum SendMode { - e_sm_fixed, - e_sm_continuous - }; - - enum NextWhat { - e_nw_stop, - e_nw_goto_next, - e_nw_goto_id - }; - - // ------------------------------------------------------- - // Methods - // ------------------------------------------------------- Stream(); ~Stream(); - // TODO: Below methods move to StreamBase??? - - bool operator < (const Stream &s) const - { return(mCore->ordinal() < s.mCore->ordinal()); } - - quint32 id() - { return mStreamId->id();} - bool setId(quint32 id) - { mStreamId->set_id(id); return true;} - -#if 0 // FIXME(HI): needed? - quint32 portId() - { return mCore->port_id();} - bool setPortId(quint32 id) - { mCore->set_port_id(id); return true;} -#endif - - quint32 ordinal() - { return mCore->ordinal();} - bool setOrdinal(quint32 ordinal) - { mCore->set_ordinal(ordinal); return true; } - - bool isEnabled() const - { return mCore->is_enabled(); } - bool setIsEnabled(bool flag) - { mCore->set_is_enabled(flag); return true; } - - const QString name() const - { return QString().fromStdString(mCore->name()); } - bool setName(QString name) - { mCore->set_name(name.toStdString()); return true; } - - // Frame Length (includes FCS) - FrameLengthMode lenMode() - { return (FrameLengthMode) mCore->len_mode(); } - bool setLenMode(FrameLengthMode lenMode) - { mCore->set_len_mode( - (OstProto::StreamCore::FrameLengthMode) lenMode); return true; } - - quint16 frameLen() - { return mCore->frame_len(); } - bool setFrameLen(quint16 frameLen) - { mCore->set_frame_len(frameLen); return true; } - - quint16 frameLenMin() - { return mCore->frame_len_min(); } - bool setFrameLenMin(quint16 frameLenMin) - { mCore->set_frame_len_min(frameLenMin); return true; } - - quint16 frameLenMax() - { return mCore->frame_len_max(); } - bool setFrameLenMax(quint16 frameLenMax) - { mCore->set_frame_len_max(frameLenMax); return true; } - - SendUnit sendUnit() - { return (SendUnit) mControl->unit(); } - bool setSendUnit(SendUnit sendUnit) - { mControl->set_unit( - (OstProto::StreamControl::SendUnit) sendUnit); return true; } - - SendMode sendMode() - { return (SendMode) mControl->mode(); } - bool setSendMode(SendMode sendMode) - { mControl->set_mode( - (OstProto::StreamControl::SendMode) sendMode); return true; } - - NextWhat nextWhat() - { return (NextWhat) mControl->next(); } - bool setNextWhat(NextWhat nextWhat) - { mControl->set_next( - (OstProto::StreamControl::NextWhat) nextWhat); return true; } - - quint32 numPackets() - { return (quint32) mControl->num_packets(); } - bool setNumPackets(quint32 numPackets) - { mControl->set_num_packets(numPackets); return true; } - - quint32 numBursts() - { return (quint32) mControl->num_bursts(); } - bool setNumBursts(quint32 numBursts) - { mControl->set_num_bursts(numBursts); return true; } - - quint32 burstSize() - { return (quint32) mControl->packets_per_burst(); } - bool setBurstSize(quint32 packetsPerBurst) - { mControl->set_packets_per_burst(packetsPerBurst); return true; } - - quint32 packetRate() - { return (quint32) mControl->packets_per_sec(); } - bool setPacketRate(quint32 packetsPerSec) - { mControl->set_packets_per_sec(packetsPerSec); return true; } - - quint32 burstRate() - { return (quint32) mControl->bursts_per_sec(); } - bool setBurstRate(quint32 burstsPerSec) - { mControl->set_bursts_per_sec(burstsPerSec); return true; } + void loadProtocolWidgets(); + void storeProtocolWidgets(); }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index eb6f76c..0c5fc73 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -1,9 +1,16 @@ #include + #include "streamconfigdialog.h" #include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" #include "modeltest.h" +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager OstProtocolManager; + int StreamConfigDialog::lastTopLevelTabIndex = 0; int StreamConfigDialog::lastProtoTabIndex = 0; @@ -15,6 +22,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, mpStream = new Stream; mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); setupUi(this); setupUiExtra(); @@ -97,8 +105,12 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Force L4 Protocol = None if L3 Protocol is set to ARP connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager.protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + LoadCurrentStream(); - mpPacketModel = new PacketModel(QList(), this); + mpPacketModel = new PacketModel(this); tvPacketTree->setModel(mpPacketModel); mpPacketModelTester = new ModelTest(mpPacketModel); tvPacketTree->header()->hide(); @@ -134,6 +146,7 @@ void StreamConfigDialog::setupUiExtra() QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); +#if 0 // MPS - temp mask // Add the Payload widget to the dialog { QGridLayout *layout; @@ -143,6 +156,7 @@ void StreamConfigDialog::setupUiExtra() layout->addWidget(mpStream->protocol(52)->configWidget(), 0, 1); qDebug("setupUi wgt = %p", mpStream->protocol(52)->configWidget()); } +#endif // ---- Setup default stuff that cannot be done in designer ---- gbVlan->setDisabled(true); @@ -199,6 +213,7 @@ StreamConfigDialog::~StreamConfigDialog() delete mpPacketModelTester; delete mpPacketModel; +#if 0 // MPS - temp mask // Remove payload data widget so that it is not deleted when this object // is destroyed { @@ -209,6 +224,7 @@ StreamConfigDialog::~StreamConfigDialog() mpStream->protocol(52)->configWidget()->setParent(0); } } +#endif // Remove any existing widget on the L2-L4 Tabs lest they are deleted // when this object is destoryed @@ -232,53 +248,95 @@ StreamConfigDialog::~StreamConfigDialog() delete bgL3Proto; delete bgL4Proto; + delete _iter; delete mpStream; } void StreamConfigDialog::updateSelectedProtocols() { - mSelectedProtocols.clear(); +#define CHKINS(p) \ +{ \ + if (_iter->hasNext() && (_iter->peekNext()->protocolNumber() == OstProto::Protocol::k##p##FieldNumber)) \ + _iter->next(); \ + else \ + _iter->insert(OstProtocolManager.createProtocol(OstProto::Protocol::k##p##FieldNumber, mpStream)); \ +} - // FIXME: Hardcoded numbers! + _iter->toFront(); +#if 1 + qDebug("Before Update"); + while (_iter->hasNext()) + { + AbstractProtocol* p; + + p = _iter->next(); + qDebug("%p:[%d]", p, p->protocolNumber()); + } + + _iter->toFront(); +#endif // Mac - mSelectedProtocols.append(51); + CHKINS(Mac) if (cbCVlan->isEnabled() && cbCVlan->isChecked()) - mSelectedProtocols.append(126); + CHKINS(Vlan); if (rbFtEthernet2->isChecked()) - mSelectedProtocols.append(121); + CHKINS(Eth2) else if (rbFt802Dot3Raw->isChecked()) - mSelectedProtocols.append(122); + CHKINS(Dot3) else if (rbFt802Dot3Llc->isChecked()) { - mSelectedProtocols.append(122); - mSelectedProtocols.append(123); + CHKINS(Dot3); + CHKINS(Llc); } else if (rbFtLlcSnap->isChecked()) { - mSelectedProtocols.append(122); - mSelectedProtocols.append(123); - mSelectedProtocols.append(124); + CHKINS(Dot3); + CHKINS(Llc); + CHKINS(Snap); } if (rbL3Ipv4->isChecked()) - mSelectedProtocols.append(130); + CHKINS(Ip4) else if (rbL3Arp->isChecked()) - mSelectedProtocols.append(131); + CHKINS(Arp) if (rbL4Tcp->isChecked()) - mSelectedProtocols.append(140); + CHKINS(Tcp) else if (rbL4Udp->isChecked()) - mSelectedProtocols.append(141); + CHKINS(Udp) else if (rbL4Icmp->isChecked()) - mSelectedProtocols.append(142); + CHKINS(Icmp) else if (rbL4Igmp->isChecked()) - mSelectedProtocols.append(143); + CHKINS(Igmp) // Payload - mSelectedProtocols.append(52); + CHKINS(Payload) + + // Remove all protocols, if any, beyond payload + while (_iter->hasNext()) + { + _iter->next(); + _iter->remove(); + } + + +#if 1 + qDebug("After Update"); + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p; + + p = _iter->next(); + qDebug("%p:[%d]", p, p->protocolNumber()); + } +#endif + +#undef CHKINS + } void StreamConfigDialog::updateContents() @@ -345,22 +403,17 @@ void StreamConfigDialog::on_pbNext_clicked() void StreamConfigDialog::on_twTopLevel_currentChanged(int index) { - QList protoList; - // We only process the "Packet View" tab if (index != 2) return; updateContents(); - foreach(int i, mSelectedProtocols) - if (mpStream->protocol(i)) - protoList.append(mpStream->protocol(i)); - - mpPacketModel->setSelectedProtocols(protoList); + mpPacketModel->setSelectedProtocols(*_iter); } void StreamConfigDialog::on_twProto_currentChanged(int index) { +#if 0 // MPS - temp mask QLayout *layout; QList wl; @@ -461,6 +514,39 @@ void StreamConfigDialog::on_twProto_currentChanged(int index) } break; } +#else + int selTab; + + qDebug("In %s (tab index = %d)", __FUNCTION__, index); + // We need to process only index 4 i.e. the Protocol Data tab + if (index != 4) + return; + + // Hide the ToolBox before modifying it - otherwise we have a crash !!! + tbProtocolData->hide(); + + selTab = tbProtocolData->currentIndex(); + + // Remove any existing widget on the activated tab + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + if (selTab < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(selTab); + + tbProtocolData->show(); +#endif } void StreamConfigDialog::update_NumPacketsAndNumBursts() @@ -504,6 +590,7 @@ void StreamConfigDialog::LoadCurrentStream() lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); } +#if 0 // MPS - temp mask // Protocols { int i; @@ -620,9 +707,130 @@ void StreamConfigDialog::LoadCurrentStream() _proto_parse_done: Q_ASSERT(i == mSelectedProtocols.size()); - mpStream->loadProtocolWidgets(); } +#else + // Protocols + { + qDebug("Loading - current list"); + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + } + _iter->toFront(); + +#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) + + Q_ASSERT(CHK(51)); // Mac + _iter->next(); + + // VLAN + if (CHK(126)) // VLAN + { + cbCVlan->setChecked(true); + _iter->next(); + } + + if (CHK(52)) // Payload + { + _iter->next(); + goto _proto_parse_done; + } + else if (CHK(121)) // Eth2 + { + rbFtEthernet2->setChecked(true); + _iter->next(); + } + else if (CHK(122)) // 802.3 RAW + { + _iter->next(); + if (CHK(123)) // 802.3 LLC + { + _iter->next(); + if (CHK(124)) // SNAP + { + rbFtLlcSnap->setChecked(true); + _iter->next(); + } + else + { + rbFt802Dot3Llc->setChecked(true); + } + } + else + { + rbFt802Dot3Raw->setChecked(true); + } + } + else + rbFtNone->setChecked(true); + + + // L3 + if (CHK(52)) // Payload + { + _iter->next(); + goto _proto_parse_done; + } + else if (CHK(130)) // IP4 + { + rbL3Ipv4->setChecked(true); + _iter->next(); + } + else if (CHK(131)) // ARP + { + rbL3Arp->setChecked(true); + _iter->next(); + } + else + rbL3None->setChecked(true); + + if (!_iter->hasNext()) + goto _proto_parse_done; + + // L4 + if (CHK(52)) // Payload + { + _iter->next(); + goto _proto_parse_done; + } + else if (CHK(140)) // TCP + { + rbL4Tcp->setChecked(true); + _iter->next(); + } + else if (CHK(141)) // UDP + { + rbL4Udp->setChecked(true); + _iter->next(); + } + else if (CHK(142)) // ICMP + { + rbL4Icmp->setChecked(true); + _iter->next(); + } + else if (CHK(143)) // IGMP + { + rbL4Igmp->setChecked(true); + _iter->next(); + } + else + rbL4None->setChecked(true); + + Q_ASSERT(CHK(52)); // Payload + _iter->next(); + +_proto_parse_done: + Q_ASSERT(!_iter->hasNext()); + mpStream->loadProtocolWidgets(); + +#undef CHK + } + + +#endif // Stream Control { @@ -673,6 +881,7 @@ _proto_parse_done: // TODO(MED): Change this when we support goto to specific stream leStreamId->setText(QString("0")); } + qDebug("loading stream done"); } void StreamConfigDialog::StoreCurrentStream() @@ -692,7 +901,7 @@ void StreamConfigDialog::StoreCurrentStream() // Protocols { updateSelectedProtocols(); - pStream->setFrameProtocol(mSelectedProtocols); + //pStream->setFrameProtocol(mSelectedProtocols); pStream->storeProtocolWidgets(); } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index fa4aa82..c0e02a7 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -27,21 +27,22 @@ public: ~StreamConfigDialog(); private: - //QList *mpStreamList; QButtonGroup *bgFrameType; QButtonGroup *bgL3Proto; QButtonGroup *bgL4Proto; + QStringListModel *mpAvailableProtocolsModel; + Port& mPort; uint mCurrentStreamIndex; - Stream *mpStream; - QList mSelectedProtocols; + + Stream *mpStream; + ProtocolListIterator *_iter; PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; - // The following static variables are used to track the "selected" tab // for the various tab widgets so that it can be restored when the dialog // is opened the next time diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index d803e44..198cbf2 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -12,6 +12,12 @@ 589 + + + 0 + 0 + + Edit Stream @@ -56,7 +62,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Frame Length (including FCS) @@ -157,10 +163,10 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + - 0 + 4 @@ -516,6 +522,20 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + Protocol Data + + + + + + -1 + + + + + diff --git a/client/streammodel.cpp b/client/streammodel.cpp index c1b887c..cba20e6 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -136,7 +136,7 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r return true; case StreamStatus: - mCurrentPort->streamByIndex(index.row())->setIsEnabled(value.toBool()); + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); emit(dataChanged(index, index)); return true; diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index b48f5b1..2bac712 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -1,5 +1,8 @@ #include + #include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" /*! \class AbstractProtocol @@ -19,13 +22,9 @@ - metaFieldCount() - isMetaField() */ -AbstractProtocol::AbstractProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : frameProtocols(frameProtoList) +AbstractProtocol::AbstractProtocol(StreamBase *stream) { - qDebug("%s: &frameproto = %p/%p (sz:%d)", __FUNCTION__, &frameProtocols, &frameProtoList, frameProtocols.size()); - stream = parent; + mpStream = stream; metaCount = -1; protoSize = -1; } @@ -34,13 +33,17 @@ AbstractProtocol::~AbstractProtocol() { } -AbstractProtocol* AbstractProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream) { return NULL; } +quint32 AbstractProtocol::protocolNumber() const +{ + qDebug("Something wrong!!!"); + return 0xFFFFFFFF; +} + /*! \fn virtual void protoDataCopyInto(OstProto::OstProto::StreamCore &stream) = 0; @@ -182,13 +185,14 @@ quint32 AbstractProtocol::protocolId(ProtocolIdType type) const quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const { quint32 id = 0xFFFFFFFF; - QLinkedListIterator iter(frameProtocols); + ProtocolListIterator *iter = mpStream->createProtocolListIterator(); - if (iter.findNext(this)) + if (iter->findNext(this)) { - if (iter.hasNext()) - id = iter.next()->protocolId(type); + if (iter->hasNext()) + id = iter->next()->protocolId(type); } + delete iter; qDebug("%s: payloadProtocolId = %u", __FUNCTION__, id); return id; @@ -215,16 +219,17 @@ int AbstractProtocol::protocolFrameSize() const int AbstractProtocol::protocolFrameOffset() const { int size = 0; - QLinkedListIterator iter(frameProtocols); + ProtocolListIterator *iter = mpStream->createProtocolListIterator(); - if (iter.findNext(this)) + if (iter->findNext(this)) { - iter.previous(); - while (iter.hasPrevious()) - size += iter.previous()->protocolFrameSize(); + iter->previous(); + while (iter->hasPrevious()) + size += iter->previous()->protocolFrameSize(); } else return -1; + delete iter; qDebug("%s: ofs = %d", __FUNCTION__, size); return size; @@ -234,15 +239,16 @@ int AbstractProtocol::protocolFramePayloadSize() const { int size = 0; - QLinkedListIterator iter(frameProtocols); + ProtocolListIterator *iter = mpStream->createProtocolListIterator(); - if (iter.findNext(this)) + if (iter->findNext(this)) { - while (iter.hasNext()) - size += iter.next()->protocolFrameSize(); + while (iter->hasNext()) + size += iter->next()->protocolFrameSize(); } else return -1; + delete iter; qDebug("%s: payloadSize = %d", __FUNCTION__, size); return size; @@ -421,17 +427,18 @@ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, CksumType cksumType) const { quint32 sum = 0xFFFF; - QLinkedListIterator iter(frameProtocols); + ProtocolListIterator *iter = mpStream->createProtocolListIterator(); Q_ASSERT(cksumType == CksumIpPseudo); - if (iter.findNext(this)) + if (iter->findNext(this)) { - iter.previous(); - if (iter.hasPrevious()) - sum = iter.previous()->protocolFrameCksum(streamIndex, + iter->previous(); + if (iter->hasPrevious()) + sum = iter->previous()->protocolFrameCksum(streamIndex, CksumIpPseudo); } + delete iter; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); @@ -444,20 +451,21 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, { quint32 sum = 0; quint16 cksum; - QLinkedListIterator iter(frameProtocols); + ProtocolListIterator *iter = mpStream->createProtocolListIterator(); Q_ASSERT(cksumType == CksumIp); - if (iter.findNext(this)) + if (iter->findNext(this)) { - while (iter.hasNext()) + while (iter->hasNext()) { - cksum = iter.next()->protocolFrameCksum(streamIndex, CksumIp); + cksum = iter->next()->protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cksum; } } else return 0; + delete iter; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index bbe8325..aca2d2d 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -9,7 +9,7 @@ #include //#include "../rpc/pbhelper.h" -#include "../common/protocol.pb.h" +#include "protocol.pb.h" #define BASE_BIN (2) #define BASE_OCT (8) @@ -19,10 +19,7 @@ #define uintToHexStr(num, bytes) \ QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) -class OstProto::StreamCore; -class AbstractProtocol; - -typedef QLinkedList ProtocolList; +class StreamBase; class AbstractProtocol { @@ -32,8 +29,7 @@ private: mutable QString protoAbbr; protected: - OstProto::StreamCore *stream; - ProtocolList &frameProtocols; + StreamBase *mpStream; public: enum FieldFlag { @@ -65,16 +61,14 @@ public: CksumMax }; - AbstractProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + AbstractProtocol(StreamBase *stream); virtual ~AbstractProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream) = 0; - virtual void protoDataCopyFrom(const OstProto::Stream &stream) = 0; + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; virtual QString name() const; virtual QString shortName() const; @@ -109,7 +103,6 @@ public: virtual void loadConfigWidget() = 0; virtual void storeConfigWidget() = 0; }; - Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); #endif diff --git a/common/dot3.cpp b/common/dot3.cpp index 448c0c5..be6d580 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -1,49 +1,49 @@ #include #include -#include "Dot3.h" +#include "dot3.h" +#include "streambase.h" #define SZ_FCS 4 -Dot3ConfigForm *Dot3Protocol::configForm = NULL; - Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -Dot3Protocol::Dot3Protocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +Dot3Protocol::Dot3Protocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new Dot3ConfigForm; + configForm = NULL; } Dot3Protocol::~Dot3Protocol() { + delete configForm; } -AbstractProtocol* Dot3Protocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream) { - return new Dot3Protocol(frameProtoList, streamCore); + return new Dot3Protocol(stream); } -void Dot3Protocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 Dot3Protocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::dot3)->CopyFrom(data); + return OstProto::Protocol::kDot3FieldNumber; } -void Dot3Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::dot3)) - data.MergeFrom(stream.GetExtension(OstProto::dot3)); + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); } QString Dot3Protocol::name() const @@ -75,14 +75,14 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, { quint16 len; - len = stream->frame_len() - SZ_FCS; + len = mpStream->frameLen() - SZ_FCS; return len; } case FieldTextValue: { quint16 len; - len = stream->frame_len() - SZ_FCS; + len = mpStream->frameLen() - SZ_FCS; return QString("%1").arg(len); } case FieldFrameValue: @@ -90,7 +90,7 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, quint16 len; QByteArray fv; - len = stream->frame_len() - SZ_FCS; + len = mpStream->frameLen() - SZ_FCS; fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; @@ -117,11 +117,15 @@ bool Dot3Protocol::setFieldData(int index, const QVariant &value, QWidget* Dot3Protocol::configWidget() { + if (configForm == NULL) + configForm = new Dot3ConfigForm; return configForm; } void Dot3Protocol::loadConfigWidget() { + configWidget(); + configForm->leLength->setText( fieldData(dot3_length, FieldValue).toString()); } @@ -130,6 +134,8 @@ void Dot3Protocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_length(configForm->leLength->text().toULong(&isOk)); } diff --git a/common/dot3.h b/common/dot3.h index 3b98b24..268e61d 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -17,7 +17,7 @@ class Dot3Protocol : public AbstractProtocol { private: OstProto::Dot3 data; - static Dot3ConfigForm *configForm; + Dot3ConfigForm *configForm; enum Dot3field { dot3_length, @@ -26,16 +26,14 @@ private: }; public: - Dot3Protocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + Dot3Protocol(StreamBase *stream); virtual ~Dot3Protocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/dot3.proto b/common/dot3.proto index 37f78ca..5a84c18 100644 --- a/common/dot3.proto +++ b/common/dot3.proto @@ -7,6 +7,6 @@ message Dot3 { optional uint32 length = 1; } -extend Stream { +extend Protocol { optional Dot3 dot3 = 122; } diff --git a/common/eth2.cpp b/common/eth2.cpp index 9fbfda6..1d72042 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -3,45 +3,44 @@ #include "eth2.h" -Eth2ConfigForm *Eth2Protocol::configForm = NULL; - Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -Eth2Protocol::Eth2Protocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +Eth2Protocol::Eth2Protocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new Eth2ConfigForm; + configForm = NULL; } Eth2Protocol::~Eth2Protocol() { + delete configForm; } -AbstractProtocol* Eth2Protocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream) { - return new Eth2Protocol(frameProtoList, streamCore); + return new Eth2Protocol(stream); } -void Eth2Protocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 Eth2Protocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::eth2)->CopyFrom(data); + return OstProto::Protocol::kEth2FieldNumber; } -void Eth2Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::eth2)) - data.MergeFrom(stream.GetExtension(OstProto::eth2)); + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); } QString Eth2Protocol::name() const @@ -121,11 +120,15 @@ bool Eth2Protocol::setFieldData(int index, const QVariant &value, QWidget* Eth2Protocol::configWidget() { + if (configForm == NULL) + configForm = new Eth2ConfigForm; return configForm; } void Eth2Protocol::loadConfigWidget() { + configWidget(); + configForm->leType->setText(uintToHexStr( fieldData(eth2_type, FieldValue).toUInt(), 2)); } @@ -134,6 +137,8 @@ void Eth2Protocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); } diff --git a/common/eth2.h b/common/eth2.h index e71659d..a99efbf 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -17,7 +17,7 @@ class Eth2Protocol : public AbstractProtocol { private: OstProto::Eth2 data; - static Eth2ConfigForm *configForm; + Eth2ConfigForm *configForm; enum eth2field { eth2_type = 0, @@ -26,16 +26,14 @@ private: }; public: - Eth2Protocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + Eth2Protocol(StreamBase *stream); virtual ~Eth2Protocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/eth2.proto b/common/eth2.proto index 224c25d..348888d 100644 --- a/common/eth2.proto +++ b/common/eth2.proto @@ -7,6 +7,6 @@ message Eth2 { optional uint32 type = 1; } -extend Stream { +extend Protocol { optional Eth2 eth2 = 121; } diff --git a/common/eth2.ui b/common/eth2.ui index a79068c..7bf6cdf 100644 --- a/common/eth2.ui +++ b/common/eth2.ui @@ -5,44 +5,35 @@ 0 0 - 166 - 72 + 267 + 64 Form - - - - - Ethernet II + + + + + Ethernet Type + + + leType - - - - - Ethernet Type - - - leType - - - - - - - false - - - >HH HH; - - - - - + + + + false + + + >HH HH; + + + + Qt::Horizontal @@ -55,6 +46,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/common/ip4.cpp b/common/ip4.cpp index 59a6773..653c83f 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -3,8 +3,6 @@ #include "ip4.h" -Ip4ConfigForm *Ip4Protocol::configForm = NULL; - Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) : QWidget(parent) { @@ -18,6 +16,11 @@ Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); } +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) { if (index == OstProto::Ip4::e_im_fixed) @@ -46,42 +49,38 @@ void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) } } -Ip4Protocol::Ip4Protocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +Ip4Protocol::Ip4Protocol(StreamBase *stream) + : AbstractProtocol(stream) { -#if 0 - PbHelper pbh; - - pbh.ForceSetSingularDefault(&data); -#endif - if (configForm == NULL) - configForm = new Ip4ConfigForm; + configForm = NULL; } Ip4Protocol::~Ip4Protocol() { + delete configForm; } -AbstractProtocol* Ip4Protocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream) { - return new Ip4Protocol(frameProtoList, streamCore); + return new Ip4Protocol(stream); } -void Ip4Protocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 Ip4Protocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::ip4)->CopyFrom(data); + return OstProto::Protocol::kIp4FieldNumber; } -void Ip4Protocol::protoDataCopyFrom(const OstProto::Stream &stream) +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::ip4)) - data.MergeFrom(stream.GetExtension(OstProto::ip4)); + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); } QString Ip4Protocol::name() const @@ -604,11 +603,15 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, QWidget* Ip4Protocol::configWidget() { + if (configForm == NULL) + configForm = new Ip4ConfigForm; return configForm; } void Ip4Protocol::loadConfigWidget() { + configWidget(); + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); @@ -649,6 +652,8 @@ void Ip4Protocol::storeConfigWidget() uint ff = 0; bool isOk; + configWidget(); + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); diff --git a/common/ip4.h b/common/ip4.h index 9c20936..a539e71 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -16,6 +16,7 @@ class Ip4ConfigForm : public QWidget, public Ui::ip4 Q_OBJECT public: Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); private slots: void on_cmbIpSrcAddrMode_currentIndexChanged(int index); void on_cmbIpDstAddrMode_currentIndexChanged(int index); @@ -25,7 +26,7 @@ class Ip4Protocol : public AbstractProtocol { private: OstProto::Ip4 data; - static Ip4ConfigForm *configForm; + Ip4ConfigForm *configForm; enum ip4field { ip4_ver = 0, @@ -58,16 +59,14 @@ private: }; public: - Ip4Protocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + Ip4Protocol(StreamBase *stream); virtual ~Ip4Protocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/ip4.proto b/common/ip4.proto index af61255..6889ffd 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -42,6 +42,6 @@ message Ip4 { // TODO: Options } -extend Stream { +extend Protocol { optional Ip4 ip4 = 130; } diff --git a/common/llc.cpp b/common/llc.cpp index c0183ca..150b8b8 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -3,45 +3,44 @@ #include "llc.h" -LlcConfigForm *LlcProtocol::configForm = NULL; - LlcConfigForm::LlcConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -LlcProtocol::LlcProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +LlcProtocol::LlcProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new LlcConfigForm; + configForm = NULL; } LlcProtocol::~LlcProtocol() { + delete configForm; } -AbstractProtocol* LlcProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream) { - return new LlcProtocol(frameProtoList, streamCore); + return new LlcProtocol(stream); } -void LlcProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 LlcProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::llc)->CopyFrom(data); + return OstProto::Protocol::kLlcFieldNumber; } -void LlcProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::llc)) - data.MergeFrom(stream.GetExtension(OstProto::llc)); + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); } QString LlcProtocol::name() const @@ -135,6 +134,8 @@ bool LlcProtocol::setFieldData(int index, const QVariant &value, QWidget* LlcProtocol::configWidget() { + if (configForm == NULL) + configForm = new LlcConfigForm; return configForm; } @@ -143,6 +144,8 @@ void LlcProtocol::loadConfigWidget() #define uintToHexStr(num, bytes) \ QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + configWidget(); + configForm->leDsap->setText(uintToHexStr( fieldData(llc_dsap, FieldValue).toUInt(), 1)); configForm->leSsap->setText(uintToHexStr( @@ -156,6 +159,8 @@ void LlcProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); diff --git a/common/llc.h b/common/llc.h index 8403fab..9fe5d28 100644 --- a/common/llc.h +++ b/common/llc.h @@ -20,7 +20,7 @@ class LlcProtocol : public AbstractProtocol { private: OstProto::Llc data; - static LlcConfigForm *configForm; + LlcConfigForm *configForm; enum llcfield { llc_dsap = 0, @@ -31,16 +31,14 @@ private: }; public: - LlcProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + LlcProtocol(StreamBase *stream); virtual ~LlcProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/llc.proto b/common/llc.proto index c57cdbe..72a4393 100644 --- a/common/llc.proto +++ b/common/llc.proto @@ -8,6 +8,6 @@ message Llc { optional uint32 ctl = 3; } -extend Stream { +extend Protocol { optional Llc llc = 123; } diff --git a/common/mac.cpp b/common/mac.cpp index 1adc9ca..dfe3ed8 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -3,8 +3,6 @@ #include "mac.h" -MacConfigForm *MacProtocol::configForm = NULL; - MacConfigForm::MacConfigForm(QWidget *parent) : QWidget(parent) { @@ -17,6 +15,11 @@ MacConfigForm::MacConfigForm(QWidget *parent) leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); } +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) { if (index == OstProto::Mac::e_mm_fixed) @@ -46,37 +49,38 @@ void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) } -MacProtocol::MacProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +MacProtocol::MacProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new MacConfigForm; + configForm = NULL; } MacProtocol::~MacProtocol() { + delete configForm; } -AbstractProtocol* MacProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream) { - return new MacProtocol(frameProtoList, streamCore); + return new MacProtocol(stream); } -void MacProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 MacProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::mac)->CopyFrom(data); + return OstProto::Protocol::kMacFieldNumber; } -void MacProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::mac)) - data.MergeFrom(stream.GetExtension(OstProto::mac)); + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); } QString MacProtocol::name() const @@ -238,11 +242,15 @@ bool MacProtocol::setFieldData(int index, const QVariant &value, QWidget* MacProtocol::configWidget() { + if (configForm == NULL) + configForm = new MacConfigForm; return configForm; } void MacProtocol::loadConfigWidget() { + configWidget(); + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); @@ -258,6 +266,8 @@ void MacProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). toULongLong(&isOk, 16)); data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> diff --git a/common/mac.h b/common/mac.h index 84199a5..a71e7cc 100644 --- a/common/mac.h +++ b/common/mac.h @@ -13,6 +13,7 @@ class MacConfigForm : public QWidget, public Ui::mac Q_OBJECT public: MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); private slots: void on_cmbDstMacMode_currentIndexChanged(int index); void on_cmbSrcMacMode_currentIndexChanged(int index); @@ -22,7 +23,7 @@ class MacProtocol : public AbstractProtocol { private: OstProto::Mac data; - static MacConfigForm *configForm; + MacConfigForm *configForm; enum macfield { mac_dstAddr = 0, @@ -39,16 +40,14 @@ private: }; public: - MacProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + MacProtocol(StreamBase *stream); virtual ~MacProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/mac.proto b/common/mac.proto index c4c5253..2e8c04e 100644 --- a/common/mac.proto +++ b/common/mac.proto @@ -24,6 +24,6 @@ message Mac { optional uint32 src_mac_step = 8 [default = 1]; } -extend Stream { +extend Protocol { optional Mac mac = 51; } diff --git a/common/mac.ui b/common/mac.ui index ca1350e..78a2c74 100644 --- a/common/mac.ui +++ b/common/mac.ui @@ -6,13 +6,13 @@ 0 0 512 - 98 + 104 Form - + @@ -81,10 +81,10 @@ false - 1 + - 1 + 0 @@ -101,10 +101,10 @@ false - 1 + - 1 + 0 @@ -164,7 +164,7 @@ false - 1 + @@ -181,16 +181,29 @@ false - 1 + - 1 + 0 + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/common/ostproto.pro b/common/ostproto.pro index 192049f..6095613 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -29,7 +29,8 @@ PROTOS += \ HEADERS += \ abstractprotocol.h \ protocolmanager.h \ - protocolcollection.h \ + protocollist.h \ + protocollistiterator.h \ streambase.h \ mac.h \ payload.h \ @@ -44,7 +45,8 @@ HEADERS += \ SOURCES += \ abstractprotocol.cpp \ protocolmanager.cpp \ - protocolcollection.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ streambase.cpp \ mac.cpp \ payload.cpp \ diff --git a/common/payload.cpp b/common/payload.cpp index ecaef9b..5633ed6 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -3,11 +3,10 @@ //#include "../client/stream.h" #include "payload.h" +#include "streambase.h" #define SZ_FCS 4 -PayloadConfigForm *PayloadProtocol::configForm = NULL; - PayloadConfigForm::PayloadConfigForm(QWidget *parent) : QWidget(parent) { @@ -31,37 +30,38 @@ void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) } } -PayloadProtocol::PayloadProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +PayloadProtocol::PayloadProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new PayloadConfigForm; + configForm = NULL; } PayloadProtocol::~PayloadProtocol() { + delete configForm; } -AbstractProtocol* PayloadProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream) { - return new PayloadProtocol(frameProtoList, streamCore); + return new PayloadProtocol(stream); } -void PayloadProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 PayloadProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::payload)->CopyFrom(data); + return OstProto::Protocol::kPayloadFieldNumber; } -void PayloadProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::payload)) - data.MergeFrom(stream.GetExtension(OstProto::payload)); + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); } QString PayloadProtocol::name() const @@ -76,7 +76,7 @@ QString PayloadProtocol::shortName() const int PayloadProtocol::protocolFrameSize() const { - return (stream->frame_len() - protocolFrameOffset() - SZ_FCS); + return (mpStream->frameLen() - protocolFrameOffset() - SZ_FCS); } int PayloadProtocol::fieldCount() const @@ -124,7 +124,7 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; int dataLen; - dataLen = stream->frame_len() - protocolFrameOffset(); + dataLen = mpStream->frameLen() - protocolFrameOffset(); dataLen -= SZ_FCS; fv.resize(dataLen+4); switch(data.pattern_mode()) @@ -179,12 +179,15 @@ bool PayloadProtocol::setFieldData(int index, const QVariant &value, QWidget* PayloadProtocol::configWidget() { + if (configForm == NULL) + configForm = new PayloadConfigForm; return configForm; - //return new PayloadConfigForm; } void PayloadProtocol::loadConfigWidget() { + configWidget(); + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); } @@ -193,6 +196,8 @@ void PayloadProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_pattern_mode((OstProto::Payload::DataPatternMode) configForm->cmbPatternMode->currentIndex()); data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); diff --git a/common/payload.h b/common/payload.h index 34e9a01..46c1d70 100644 --- a/common/payload.h +++ b/common/payload.h @@ -19,7 +19,7 @@ class PayloadProtocol : public AbstractProtocol { private: OstProto::Payload data; - static PayloadConfigForm *configForm; + PayloadConfigForm *configForm; enum payloadfield { payload_dataPattern, @@ -31,16 +31,14 @@ private: }; public: - PayloadProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + PayloadProtocol(StreamBase *stream); virtual ~PayloadProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/payload.proto b/common/payload.proto index e97f33c..b3a6946 100644 --- a/common/payload.proto +++ b/common/payload.proto @@ -17,6 +17,6 @@ message Payload { //optional uint32 data_start_ofs = 13; } -extend Stream { +extend Protocol { optional Payload payload = 52; } diff --git a/common/protocol.proto b/common/protocol.proto index a809795..9788735 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -28,7 +28,7 @@ message StreamCore { optional uint32 frame_len_max = 17 [default = 1518]; // Currently Selected Protocols - repeated uint32 frame_proto = 20; + //repeated uint32 frame_proto = 20; } message StreamControl { @@ -61,14 +61,45 @@ message StreamControl { } +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 51 to 100; // Reserved for Ostinato Use + extensions 101 to 200; // Available for use by protocols + + enum k { + kMacFieldNumber = 51; + kPayloadFieldNumber = 52; + + kEth2FieldNumber = 121; + kDot3FieldNumber = 122; + kLlcFieldNumber = 123; + kSnapFieldNumber = 124; + + kVlanFieldNumber = 126; + + kIp4FieldNumber = 130; + kArpFieldNumber = 131; + + kTcpFieldNumber = 140; + kUdpFieldNumber = 141; + kIcmpFieldNumber = 142; + kIgmpFieldNumber = 143; + } +} + message Stream { required StreamId stream_id = 1; optional StreamCore core = 2; optional StreamControl control = 3; - extensions 51 to 100; // Reserved for Ostinato Use - extensions 101 to 200; // Available for use by protocols + repeated Protocol protocol = 4; } message Void { diff --git a/common/protocolcollection.cpp b/common/protocolcollection.cpp deleted file mode 100644 index 60e6d21..0000000 --- a/common/protocolcollection.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "protocolcollection.h" - -extern ProtocolManager OstProtocolManager; - -ProtocolCollection::ProtocolCollection(ProtocolList &streamProtocols, - OstProto::StreamCore *streamCore) - : protoManager(OstProtocolManager) -{ - // Create an instance of each registered protocol - - QMapIterator iter(protoManager.factory); - - while (iter.hasNext()) - { - AbstractProtocol* (*p)(ProtocolList&, OstProto::StreamCore*); - AbstractProtocol* q; - - iter.next(); - p = (AbstractProtocol* (*)(ProtocolList&, OstProto::StreamCore*)) - iter.value(); - q = (*p)(streamProtocols, streamCore); - - protocols.insert(iter.key(), q); - } -} - -ProtocolCollection::~ProtocolCollection() -{ - QMutableMapIterator iter(protocols); - - while (iter.hasNext()) - { - iter.next(); - if (iter.value()) - { - delete iter.value(); - iter.remove(); - } - } -} - -void ProtocolCollection::protoDataCopyFrom(const OstProto::Stream &stream) const -{ - QMapIterator iter(protocols); - - while (iter.hasNext()) - { - iter.next(); - if (iter.value()) - { - iter.value()->protoDataCopyFrom(stream); - } - } -} - -void ProtocolCollection::protoDataCopyInto(OstProto::Stream &stream) const -{ - QMapIterator iter(protocols); - - while (iter.hasNext()) - { - iter.next(); - if (iter.value()) - { - iter.value()->protoDataCopyInto(stream); - } - } -} - -void ProtocolCollection::loadConfigWidgets() const -{ - QMapIterator iter(protocols); - - while (iter.hasNext()) - { - iter.next(); - if (iter.value()) - { - iter.value()->loadConfigWidget(); - } - } -} - -void ProtocolCollection::storeConfigWidgets() const -{ - QMapIterator iter(protocols); - - while (iter.hasNext()) - { - iter.next(); - if (iter.value()) - { - iter.value()->storeConfigWidget(); - } - } -} - -AbstractProtocol* ProtocolCollection::protocol(int protoNum) -{ - return protocols.value(protoNum); -} - -AbstractProtocol* ProtocolCollection::protocol(QString protoName) -{ - return protocols.value(protoManager.nameToNumberMap.value(protoName)); -} diff --git a/common/protocolcollection.h b/common/protocolcollection.h deleted file mode 100644 index 61ca418..0000000 --- a/common/protocolcollection.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _PROTOCOL_COLLECTION_H -#define _PROTOCOL_COLLECTION_H - -#include -#include - -#include "abstractprotocol.h" -#include "protocolmanager.h" - -class ProtocolCollection { - - ProtocolManager &protoManager; - QMap protocols; - -public: - ProtocolCollection(ProtocolList &streamProtocols, - OstProto::StreamCore *streamCore); - ProtocolCollection::~ProtocolCollection(); - - void protoDataCopyFrom(const OstProto::Stream &stream) const; - void protoDataCopyInto(OstProto::Stream &stream) const; - - void loadConfigWidgets() const; - void storeConfigWidgets() const; - - AbstractProtocol* protocol(int protoNum); - AbstractProtocol* protocol(QString protoName); -}; - -#endif diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..26d2aab --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,8 @@ +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..bbf2720 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,9 @@ +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..20a08ec --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,87 @@ +#include "protocollistiterator.h" +#include "protocollist.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext((AbstractProtocol*)((uint)value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious((AbstractProtocol*)((uint)value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(const AbstractProtocol* value) +{ + _iter->insert((AbstractProtocol*)((uint)value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + _iter->remove(); +} + +void ProtocolListIterator::setValue(const AbstractProtocol* value) const +{ + _iter->setValue((AbstractProtocol*)((uint)value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..c2dffd3 --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,29 @@ +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(const AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(const AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 9041adc..a353796 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -1,5 +1,9 @@ #include "protocolmanager.h" +// FIXME(HI): remove +#include "protocol.pb.h" +#include "abstractprotocol.h" + #include "mac.h" #include "payload.h" @@ -12,9 +16,6 @@ #include "tcp.h" #include "udp.h" -QMap ProtocolManager::factory; -QMap ProtocolManager::nameToNumberMap; - ProtocolManager OstProtocolManager; ProtocolManager::ProtocolManager() @@ -36,5 +37,33 @@ void ProtocolManager::registerProtocol(int protoNumber, QString protoName, { // TODO: validate incoming params for duplicates with existing nameToNumberMap.insert(protoName, protoNumber); + numberToNameMap.insert(protoNumber, protoName); factory.insert(protoNumber, protoInstanceCreator); } + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream) +{ + AbstractProtocol* (*pc)(StreamBase*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream) +{ + return createProtocol(nameToNumberMap.value(protoName), stream); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h index de602fc..ef0a605 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -2,18 +2,27 @@ #define _PROTOCOL_MANAGER_H #include +#include + +class AbstractProtocol; +class StreamBase; class ProtocolManager { -public: - //! \todo Make these data structures private/protected - static QMap nameToNumberMap; - static QMap factory; + QMap numberToNameMap; + QMap nameToNumberMap; + QMap factory; public: ProtocolManager(); + void registerProtocol(int protoNumber, QString protoName, void *protoCreator); + + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream); + + QStringList protocolDatabase(); }; #endif diff --git a/common/sample.cpp b/common/sample.cpp index e92c9fc..17ff0c3 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -3,46 +3,44 @@ #include "sample.h" -SampleConfigForm *SampleProtocol::configForm = NULL; - SampleConfigForm::SampleConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); - } -SampleProtocol::SampleProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +SampleProtocol::SampleProtocol(StreamBase *stream); + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new SampleConfigForm; + configForm = NULL; } SampleProtocol::~SampleProtocol() { + delete configForm; } -AbstractProtocol* SampleProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream) { return new SampleProtocol(frameProtoList, streamCore); } -void SampleProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 SampleProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::sample)->CopyFrom(data); + return OstProto::Protocol::kSampleFieldNumber; } -void SampleProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::sample)) - data.MergeFrom(stream.GetExtension(OstProto::sample)); + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()) +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id()->id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); } QString SampleProtocol::name() const @@ -156,15 +154,21 @@ bool SampleProtocol::setFieldData(int index, const QVariant &value, QWidget* SampleProtocol::configWidget() { + if (configForm == NULL) + configFrom = new SampleConfigForm; + return configForm; } void SampleProtocol::loadConfigWidget() { + configWidget(); } void SampleProtocol::storeConfigWidget() { bool isOk; + + configWidget(); } diff --git a/common/sample.h b/common/sample.h index 9da187d..e02b6bc 100644 --- a/common/sample.h +++ b/common/sample.h @@ -18,7 +18,7 @@ class SampleProtocol : public AbstractProtocol { private: OstProto::Sample data; - static SampleConfigForm *configForm; + SampleConfigForm *configForm; enum samplefield { @@ -26,16 +26,14 @@ private: }; public: - SampleProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + SampleProtocol(StreamBase *stream); virtual ~SampleProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/snap.cpp b/common/snap.cpp index 46cca1e..0aca641 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -3,45 +3,44 @@ #include "snap.h" -SnapConfigForm *SnapProtocol::configForm = NULL; - SnapConfigForm::SnapConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -SnapProtocol::SnapProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +SnapProtocol::SnapProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new SnapConfigForm; + configForm = NULL; } SnapProtocol::~SnapProtocol() { + delete configForm; } -AbstractProtocol* SnapProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream) { - return new SnapProtocol(frameProtoList, streamCore); + return new SnapProtocol(stream); } -void SnapProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 SnapProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::snap)->CopyFrom(data); + return OstProto::Protocol::kSnapFieldNumber; } -void SnapProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::snap)) - data.MergeFrom(stream.GetExtension(OstProto::snap)); + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); } QString SnapProtocol::name() const @@ -140,11 +139,15 @@ bool SnapProtocol::setFieldData(int index, const QVariant &value, QWidget* SnapProtocol::configWidget() { + if (configForm == NULL) + configForm = new SnapConfigForm; return configForm; } void SnapProtocol::loadConfigWidget() { + configWidget(); + configForm->leOui->setText(uintToHexStr( fieldData(snap_oui, FieldValue).toUInt(), 3)); configForm->leType->setText(uintToHexStr( @@ -155,6 +158,8 @@ void SnapProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); } diff --git a/common/snap.h b/common/snap.h index ba0a3b6..79e2780 100644 --- a/common/snap.h +++ b/common/snap.h @@ -17,7 +17,7 @@ class SnapProtocol : public AbstractProtocol { private: OstProto::Snap data; - static SnapConfigForm *configForm; + SnapConfigForm *configForm; enum snapfield { snap_oui = 0, @@ -27,16 +27,14 @@ private: }; public: - SnapProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + SnapProtocol(StreamBase *stream); virtual ~SnapProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/snap.proto b/common/snap.proto index c5ea432..957c213 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -7,6 +7,6 @@ message Snap { optional uint32 type = 2; } -extend Stream { +extend Protocol { optional Snap snap = 124; } diff --git a/common/streambase.cpp b/common/streambase.cpp index 4bd4faf..b98c6c6 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -1,12 +1,47 @@ #include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager OstProtocolManager; StreamBase::StreamBase() : mStreamId(new OstProto::StreamId), mCore(new OstProto::StreamCore), - mControl(new OstProto::StreamControl), - protocols(currentFrameProtocols, mCore) + mControl(new OstProto::StreamControl) { + AbstractProtocol *proto; + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager.createProtocol("mac", this); + currentFrameProtocols->append(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager.createProtocol("payload", this); + currentFrameProtocols->append(proto); + qDebug("stream: payload = %p", proto); + + { + ProtocolListIterator *iter = createProtocolListIterator(); + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + delete iter; + } } StreamBase::~StreamBase() @@ -18,12 +53,20 @@ StreamBase::~StreamBase() void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) { + AbstractProtocol *proto; + mStreamId->CopyFrom(stream.stream_id()); mCore->CopyFrom(stream.core()); mControl->CopyFrom(stream.control()); - protocols.protoDataCopyFrom(stream); - setFrameProtocol(frameProtocol()); + currentFrameProtocols->destroy(); + for (int i=0; i < stream.protocol_size(); i++) + { + proto = OstProtocolManager.createProtocol( + stream.protocol(i).protocol_id().id(), this); + proto->protoDataCopyFrom(stream.protocol(i)); + currentFrameProtocols->append(proto); + } } void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const @@ -32,38 +75,211 @@ void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const stream.mutable_core()->CopyFrom(*mCore); stream.mutable_control()->CopyFrom(*mControl); - protocols.protoDataCopyInto(stream); -} - -QList StreamBase::frameProtocol() -{ - QList protocolList; - - for (int i = 0; i < mCore->frame_proto_size(); i++) - protocolList.append(mCore->frame_proto(i)); - - return protocolList; -} - -void StreamBase::setFrameProtocol(QList protocolList) -{ - mCore->clear_frame_proto(); - currentFrameProtocols.clear(); - - for (int i = 0; i < protocolList.size(); i++) + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) { - mCore->add_frame_proto(protocolList.at(i)); - currentFrameProtocols.append(protocols.protocol(protocolList.at(i))); + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); } } -AbstractProtocol* StreamBase::protocol(int protoNum) +#if 0 +ProtocolList StreamBase::frameProtocol() { - return protocols.protocol(protoNum); + return currentFrameProtocols; } -AbstractProtocol* StreamBase::protocol(QString protoName) +void StreamBase::setFrameProtocol(ProtocolList protocolList) { - return protocols.protocol(protoName); + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() +{ + return new ProtocolListIterator(*currentFrameProtocols); } +bool StreamBase::operator < (const StreamBase &s) const +{ + return(mCore->ordinal() < s.mCore->ordinal()); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen() +{ + return mCore->frame_len(); +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +StreamBase::SendUnit StreamBase::sendUnit() +{ + return (StreamBase::SendUnit) mControl->unit(); +} +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +quint32 StreamBase::packetRate() +{ + return (quint32) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(quint32 packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +quint32 StreamBase::burstRate() +{ + return (quint32) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(quint32 burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} diff --git a/common/streambase.h b/common/streambase.h index c8eb2c8..99838d9 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -1,21 +1,25 @@ #ifndef _STREAM_BASE_H #define _STREAM_BASE_H -#include +#include +#include -#include "protocolcollection.h" +#include "protocol.pb.h" + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; class StreamBase { -protected: // TODO: temp - make private +private: OstProto::StreamId *mStreamId; OstProto::StreamCore *mCore; OstProto::StreamControl *mControl; -private: - ProtocolList currentFrameProtocols; protected: - ProtocolCollection protocols; + //! \todo TODO: Make ProtocolList a private member of StreamBase? + ProtocolList *currentFrameProtocols; public: StreamBase(); @@ -24,13 +28,91 @@ public: void protoDataCopyFrom(const OstProto::Stream &stream); void protoDataCopyInto(OstProto::Stream &stream) const; - QList frameProtocol(); - void setFrameProtocol(QList protocolList); - - AbstractProtocol* protocol(int protoNum); - AbstractProtocol* protocol(QString protoName); + ProtocolListIterator* createProtocolListIterator(); // TODO: make a copy constructor + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + bool operator < (const StreamBase &s) const; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode(); + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(); + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin(); + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax(); + bool setFrameLenMax(quint16 frameLenMax); + + SendUnit sendUnit(); + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode(); + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat(); + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets(); + bool setNumPackets(quint32 numPackets); + + quint32 numBursts(); + bool setNumBursts(quint32 numBursts); + + quint32 burstSize(); + bool setBurstSize(quint32 packetsPerBurst); + + quint32 packetRate(); + bool setPacketRate(quint32 packetsPerSec); + + quint32 burstRate(); + bool setBurstRate(quint32 burstsPerSec); }; #endif diff --git a/common/tcp.cpp b/common/tcp.cpp index ab13d54..c3b732b 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -3,45 +3,44 @@ #include "tcp.h" -TcpConfigForm *TcpProtocol::configForm = NULL; - TcpConfigForm::TcpConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -TcpProtocol::TcpProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +TcpProtocol::TcpProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new TcpConfigForm; + configForm = NULL; } TcpProtocol::~TcpProtocol() { + delete configForm; } -AbstractProtocol* TcpProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream) { - return new TcpProtocol(frameProtoList, streamCore); + return new TcpProtocol(stream); } -void TcpProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 TcpProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::tcp)->CopyFrom(data); + return OstProto::Protocol::kTcpFieldNumber; } -void TcpProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::tcp)) - data.MergeFrom(stream.GetExtension(OstProto::tcp)); + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); } QString TcpProtocol::name() const @@ -384,11 +383,15 @@ bool TcpProtocol::setFieldData(int index, const QVariant &value, QWidget* TcpProtocol::configWidget() { + if (configForm == NULL) + configForm = new TcpConfigForm; return configForm; } void TcpProtocol::loadConfigWidget() { + configWidget(); + configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); @@ -419,6 +422,8 @@ void TcpProtocol::storeConfigWidget() bool isOk; int ff = 0; + configWidget(); + data.set_src_port(configForm->leTcpSrcPort->text().toULong(&isOk)); data.set_dst_port(configForm->leTcpDstPort->text().toULong(&isOk)); diff --git a/common/tcp.h b/common/tcp.h index bfe165b..36edba8 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -24,7 +24,7 @@ class TcpProtocol : public AbstractProtocol { private: OstProto::Tcp data; - static TcpConfigForm *configForm; + TcpConfigForm *configForm; enum tcpfield { tcp_src_port = 0, @@ -45,16 +45,14 @@ private: }; public: - TcpProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + TcpProtocol(StreamBase *stream); virtual ~TcpProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/tcp.proto b/common/tcp.proto index 8144bad..3cc6135 100644 --- a/common/tcp.proto +++ b/common/tcp.proto @@ -21,7 +21,7 @@ message Tcp { optional uint32 urg_ptr = 11; } -extend Stream { +extend Protocol { optional Tcp tcp = 140; } diff --git a/common/udp.cpp b/common/udp.cpp index 4c642c2..9baae78 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -3,45 +3,44 @@ #include "udp.h" -UdpConfigForm *UdpProtocol::configForm = NULL; - UdpConfigForm::UdpConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -UdpProtocol::UdpProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +UdpProtocol::UdpProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new UdpConfigForm; + configForm = NULL; } UdpProtocol::~UdpProtocol() { + delete configForm; } -AbstractProtocol* UdpProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream) { - return new UdpProtocol(frameProtoList, streamCore); + return new UdpProtocol(stream); } -void UdpProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 UdpProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::udp)->CopyFrom(data); + return OstProto::Protocol::kUdpFieldNumber; } -void UdpProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::udp)) - data.MergeFrom(stream.GetExtension(OstProto::udp)); + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); } QString UdpProtocol::name() const @@ -261,11 +260,15 @@ bool UdpProtocol::setFieldData(int index, const QVariant &value, QWidget* UdpProtocol::configWidget() { + if (configForm == NULL) + configForm = new UdpConfigForm; return configForm; } void UdpProtocol::loadConfigWidget() { + configWidget(); + configForm->leUdpSrcPort->setText(fieldData(udp_srcPort, FieldValue).toString()); configForm->leUdpDstPort->setText(fieldData(udp_dstPort, FieldValue).toString()); @@ -281,6 +284,8 @@ void UdpProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_src_port(configForm->leUdpSrcPort->text().toULong(&isOk)); data.set_dst_port(configForm->leUdpDstPort->text().toULong(&isOk)); diff --git a/common/udp.h b/common/udp.h index 0fea509..a2abbed 100644 --- a/common/udp.h +++ b/common/udp.h @@ -17,7 +17,7 @@ class UdpProtocol : public AbstractProtocol { private: OstProto::Udp data; - static UdpConfigForm *configForm; + UdpConfigForm *configForm; enum udpfield { udp_srcPort = 0, @@ -32,16 +32,14 @@ private: }; public: - UdpProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + UdpProtocol(StreamBase *stream); virtual ~UdpProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/udp.proto b/common/udp.proto index 5f9680d..89e3065 100644 --- a/common/udp.proto +++ b/common/udp.proto @@ -13,6 +13,6 @@ message Udp { optional uint32 cksum = 6; } -extend Stream { +extend Protocol { optional Udp udp = 141; } diff --git a/common/vlan.cpp b/common/vlan.cpp index 7908e8e..f261264 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -2,45 +2,44 @@ #include "vlan.h" -VlanConfigForm *VlanProtocol::configForm = NULL; - VlanConfigForm::VlanConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -VlanProtocol::VlanProtocol( - ProtocolList &frameProtoList, - OstProto::StreamCore *parent) - : AbstractProtocol(frameProtoList, parent) +VlanProtocol::VlanProtocol(StreamBase *stream) + : AbstractProtocol(stream) { - if (configForm == NULL) - configForm = new VlanConfigForm; + configForm = NULL; } VlanProtocol::~VlanProtocol() { + delete configForm; } -AbstractProtocol* VlanProtocol::createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore) +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream) { - return new VlanProtocol(frameProtoList, streamCore); + return new VlanProtocol(stream); } -void VlanProtocol::protoDataCopyInto(OstProto::Stream &stream) +quint32 VlanProtocol::protocolNumber() const { - // FIXME: multiple headers - stream.MutableExtension(OstProto::vlan)->CopyFrom(data); + return OstProto::Protocol::kVlanFieldNumber; } -void VlanProtocol::protoDataCopyFrom(const OstProto::Stream &stream) +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME: multiple headers - if (stream.HasExtension(OstProto::vlan)) - data.MergeFrom(stream.GetExtension(OstProto::vlan)); + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); } QString VlanProtocol::name() const @@ -203,22 +202,27 @@ bool VlanProtocol::setFieldData(int index, const QVariant &value, QWidget* VlanProtocol::configWidget() { + if (configForm == NULL) + configForm = new VlanConfigForm; return configForm; } void VlanProtocol::loadConfigWidget() { + configWidget(); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); - } void VlanProtocol::storeConfigWidget() { bool isOk; + configWidget(); + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); data.set_vlan_tag( diff --git a/common/vlan.h b/common/vlan.h index 23edf44..fedf315 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -17,7 +17,7 @@ class VlanProtocol : public AbstractProtocol { private: OstProto::Vlan data; - static VlanConfigForm *configForm; + VlanConfigForm *configForm; enum Vlanfield { vlan_tpid, @@ -32,16 +32,14 @@ private: }; public: - VlanProtocol(ProtocolList &frameProtoList, - OstProto::StreamCore *parent = 0); + VlanProtocol(StreamBase *stream); virtual ~VlanProtocol(); - static AbstractProtocol* createInstance( - ProtocolList &frameProtoList, - OstProto::StreamCore *streamCore = 0); + static AbstractProtocol* createInstance(StreamBase *stream); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Stream &stream); - virtual void protoDataCopyFrom(const OstProto::Stream &stream); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; diff --git a/common/vlan.proto b/common/vlan.proto index 6703dbc..0232612 100644 --- a/common/vlan.proto +++ b/common/vlan.proto @@ -10,6 +10,6 @@ message Vlan { optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid } -extend Stream { +extend Protocol { optional Vlan vlan = 126; } diff --git a/server/myservice.cpp b/server/myservice.cpp index 546fda5..6d9d2ae 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,8 +1,12 @@ -#include "myservice.h" -#include "qdebug.h" #include #include +#include "qdebug.h" + +#include "myservice.h" +#include "../common/protocollist.h" +#include "../common/abstractprotocol.h" + #if 0 #include @@ -26,50 +30,31 @@ StreamInfo::~StreamInfo() { } -#if 0 -quint32 StreamInfo::pseudoHdrCksumPartial(quint32 srcIp, quint32 dstIp, - quint8 protocol, quint16 len) -{ - quint32 sum; - - sum = srcIp >> 16; - sum += srcIp & 0xFFFF; - sum += dstIp >> 16; - sum += dstIp & 0xFFFF; - sum += (quint16) (protocol); - sum += len; - - // Above calculation done assuming 'big endian' - so convert to host order - return qFromBigEndian(sum); -} -#endif - - int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) { int pktLen, len = 0; // Decide a frame length based on length mode - switch(mCore->len_mode()) + switch(lenMode()) { case OstProto::StreamCore::e_fl_fixed: - pktLen = mCore->frame_len(); + pktLen = frameLen(); break; case OstProto::StreamCore::e_fl_inc: - pktLen = mCore->frame_len_min() + (n % - (mCore->frame_len_max() - mCore->frame_len_min() + 1)); + pktLen = frameLenMin() + (n % + (frameLenMax() - frameLenMin() + 1)); break; case OstProto::StreamCore::e_fl_dec: - pktLen = mCore->frame_len_max() - (n % - (mCore->frame_len_max() - mCore->frame_len_min() + 1)); + pktLen = frameLenMax() - (n % + (frameLenMax() - frameLenMin() + 1)); break; case OstProto::StreamCore::e_fl_random: - pktLen = mCore->frame_len_min() + (qrand() % - (mCore->frame_len_max() - mCore->frame_len_min() + 1)); + pktLen = frameLenMin() + (qrand() % + (frameLenMax() - frameLenMin() + 1)); break; default: qWarning("Unhandled len mode %d. Using default 64", - mCore->len_mode()); + lenMode()); pktLen = 64; break; } @@ -81,11 +66,11 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) return 0; // FIXME: Calculated pktLen is an input to Payload Protocol - for (int i = 0; i < mCore->frame_proto_size(); i++) + foreach(const AbstractProtocol* proto, *currentFrameProtocols) { QByteArray ba; - ba = protocol(mCore->frame_proto(i))->protocolFrameValue(n); + ba = proto->protocolFrameValue(n); if (len + ba.size() < bufMaxSize) { memcpy(buf+len, ba.constData(), ba.size()); @@ -94,379 +79,6 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) } return pktLen; - -#if 0 // Proto FW - int u, pktLen, dataLen, len = 0; - quint32 srcIp, dstIp; // need it later for TCP/UDP cksum calculation - quint32 cumCksum = 0; // cumulative cksum used to combine partial cksums - int tcpOfs, udpOfs; // needed to fill in cksum later - uchar scratch[8]; - - // We always have a Mac Header! - switch (d.mac().dst_mac_mode()) - { - case OstProto::Mac::e_mm_fixed: - qToBigEndian((quint64) d.mac().dst_mac(), scratch); - break; - case OstProto::Mac::e_mm_inc: - u = (n % d.mac().dst_mac_count()) * d.mac().dst_mac_step(); - qToBigEndian((quint64) d.mac().dst_mac() + u, scratch); - break; - case OstProto::Mac::e_mm_dec: - u = (n % d.mac().dst_mac_count()) * d.mac().dst_mac_step(); - qToBigEndian((quint64) d.mac().dst_mac() - u, scratch); - break; - default: - qWarning("Unhandled dstMac_mode %d", d.mac().dst_mac_mode()); - } - memcpy((buf + len), scratch + 2, 6); - len += 6; - - switch (d.mac().src_mac_mode()) - { - case OstProto::Mac::e_mm_fixed: - qToBigEndian((quint64) d.mac().src_mac(), scratch); - break; - case OstProto::Mac::e_mm_inc: - u = (n % d.mac().src_mac_count()) * d.mac().src_mac_step(); - qToBigEndian((quint64) d.mac().src_mac() + u, scratch); - break; - case OstProto::Mac::e_mm_dec: - u = (n % d.mac().src_mac_count()) * d.mac().src_mac_step(); - qToBigEndian((quint64) d.mac().src_mac() - u, scratch); - break; - default: - qWarning("Unhandled srcMac_mode %d", d.mac().src_mac_mode()); - } - memcpy((buf + len), scratch + 2, 6); - len += 6; - - - // Frame Type - Part 1 (pre VLAN info) - switch(d.core().ft()) - { - case OstProto::StreamCore::e_ft_none: - case OstProto::StreamCore::e_ft_eth_2: - break; - case OstProto::StreamCore::e_ft_802_3_raw: - qToBigEndian((quint16) pktLen, buf+len); - len += 2; - break; - case OstProto::StreamCore::e_ft_802_3_llc: - qToBigEndian((quint16) pktLen, buf+len); - len += 2; - buf[len+0] = (quint8) d.llc().dsap(); - buf[len+1] = (quint8) d.llc().ssap(); - buf[len+2] = (quint8) d.llc().ctl(); - len +=3; - break; - case OstProto::StreamCore::e_ft_snap: - qToBigEndian((quint16) pktLen, buf+len); - len += 2; - buf[len+0] = (quint8) d.llc().dsap(); - buf[len+1] = (quint8) d.llc().ssap(); - buf[len+2] = (quint8) d.llc().ctl(); - len +=3; - qToBigEndian((quint32) d.snap().oui(), scratch); - memcpy((buf + len), scratch + 2, 3); - len += 3; - break; - default: - qWarning("Unhandled frame type %d\n", d.core().ft()); - } - - // VLAN - if (d.vlan().is_svlan_tagged()) - { - if (d.vlan().is_stpid_override()) - qToBigEndian((quint16) d.vlan().stpid(), buf+len); - else - qToBigEndian((quint16) 0x88a8, buf+len); - len += 2 ; - - qToBigEndian((quint16) d.vlan().svlan_tag(), buf+len); - len += 2 ; - } - - if (d.vlan().is_cvlan_tagged()) - { - if (d.vlan().is_ctpid_override()) - qToBigEndian((quint16) d.vlan().ctpid(), buf+len); - else - qToBigEndian((quint16) 0x8100, buf+len); - len += 2 ; - - qToBigEndian((quint16) d.vlan().cvlan_tag(), buf+len); - len += 2 ; - } - - // Frame Type - Part 2 (post VLAN info) - switch(d.core().ft()) - { - case OstProto::StreamCore::e_ft_none: - break; - case OstProto::StreamCore::e_ft_eth_2: - qToBigEndian((quint16) d.eth2().type(), buf+len); - len += 2; - break; - case OstProto::StreamCore::e_ft_802_3_raw: - case OstProto::StreamCore::e_ft_802_3_llc: - break; - case OstProto::StreamCore::e_ft_snap: - qToBigEndian((quint16) d.eth2().type(), buf+len); - len += 2; - break; - default: - qWarning("Unhandled frame type %d\n", d.core().ft()); - } - - // L3 - switch (d.core().l3_proto()) - { - case OstProto::StreamCore::e_l3_none: - break; - case OstProto::StreamCore::e_l3_ip: - { - quint32 subnet, host; - int ipOfs = len; - - buf[len+0] = (quint8) (d.ip().ver_hdrlen()); - buf[len+1] = (quint8) (d.ip().tos()); - len += 2; - - if (d.ip().is_override_totlen()) - qToBigEndian((quint16) d.ip().tot_len(), buf+len); - else - qToBigEndian((quint16) (pktLen - ipOfs), buf+len); - len += 2; - - qToBigEndian((quint16) d.ip().id(), buf+len); - len += 2; - - qToBigEndian((quint16) (( (d.ip().flags() & 0x3) << 13) | - (d.ip().frag_ofs() & 0x1FFF)), buf+len); - len += 2; - - buf[len+0] = (quint8) (d.ip().ttl()); - buf[len+1] = (quint8) (d.ip().proto()); - len += 2; - - // cksum calculated after filling in the rest - qToBigEndian((quint16) 0, buf+len); - len += 2; - - // Get Src/Dst IP for this packet using respective IpMode - switch(d.ip().src_ip_mode()) - { - case OstProto::Ip::e_im_fixed: - srcIp = (quint32) d.ip().src_ip(); - qToBigEndian(srcIp, buf+len); - break; - case OstProto::Ip::e_im_inc_host: - u = n % d.ip().src_ip_count(); - subnet = d.ip().src_ip() & d.ip().src_ip_mask(); - host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) + u) & - ~d.ip().src_ip_mask()); - srcIp = (quint32) (subnet | host); - qToBigEndian(srcIp, buf+len); - break; - case OstProto::Ip::e_im_dec_host: - u = n % d.ip().src_ip_count(); - subnet = d.ip().src_ip() & d.ip().src_ip_mask(); - host = (((d.ip().src_ip() & ~d.ip().src_ip_mask()) - u) & - ~d.ip().src_ip_mask()); - srcIp = (quint32) (subnet | host); - qToBigEndian(srcIp, buf+len); - break; - case OstProto::Ip::e_im_random_host: - subnet = d.ip().src_ip() & d.ip().src_ip_mask(); - host = (qrand() & ~d.ip().src_ip_mask()); - srcIp = (quint32) (subnet | host); - qToBigEndian(srcIp, buf+len); - break; - default: - qWarning("Unhandled src_ip_mode = %d", d.ip().src_ip_mode()); - } - len +=4; - - switch(d.ip().dst_ip_mode()) - { - case OstProto::Ip::e_im_fixed: - dstIp = (quint32) d.ip().dst_ip(); - qToBigEndian(dstIp, buf+len); - break; - case OstProto::Ip::e_im_inc_host: - u = n % d.ip().dst_ip_count(); - subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); - host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) + u) & - ~d.ip().dst_ip_mask()); - dstIp = (quint32) (subnet | host); - qToBigEndian(dstIp, buf+len); - break; - case OstProto::Ip::e_im_dec_host: - u = n % d.ip().dst_ip_count(); - subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); - host = (((d.ip().dst_ip() & ~d.ip().dst_ip_mask()) - u) & - ~d.ip().dst_ip_mask()); - dstIp = (quint32) (subnet | host); - qToBigEndian(dstIp, buf+len); - break; - case OstProto::Ip::e_im_random_host: - subnet = d.ip().dst_ip() & d.ip().dst_ip_mask(); - host = (qrand() & ~d.ip().dst_ip_mask()); - dstIp = (quint32) (subnet | host); - qToBigEndian(dstIp, buf+len); - break; - default: - qWarning("Unhandled dst_ip_mode = %d", d.ip().dst_ip_mode()); - } - len +=4; - - // Calculate and fill in cksum (unless overridden) - if (d.ip().is_override_cksum()) - qToBigEndian((quint16) d.ip().cksum(), buf+ipOfs+10); - else - *((quint16*)(buf + ipOfs + 10)) = ipv4Cksum(buf + ipOfs, len-ipOfs); - break; - - } - case OstProto::StreamCore::e_l3_arp: - // TODO(LOW) - break; - default: - qWarning("Unhandled l3 proto %d\n", d.core().l3_proto()); - } - - switch (d.core().l4_proto()) - { - case OstProto::StreamCore::e_l4_none: - break; - case OstProto::StreamCore::e_l4_tcp: - { - tcpOfs = len; - - cumCksum = pseudoHdrCksumPartial(srcIp, dstIp, 6, pktLen - len); - - qToBigEndian((quint16) d.tcp().src_port(), buf+len); - len += 2; - qToBigEndian((quint16) d.tcp().dst_port(), buf+len); - len += 2; - - qToBigEndian((quint32) d.tcp().seq_num(), buf+len); - len += 4; - qToBigEndian((quint32) d.tcp().ack_num(), buf+len); - len += 4; - - if (d.tcp().is_override_hdrlen()) - buf[len+0] = (quint8) d.tcp().hdrlen_rsvd(); - else - buf[len+0] = (quint8) 0x50; // FIXME(LOW): Hardcoding - buf[len+1] = (quint8) d.tcp().flags(); - len += 2; - - qToBigEndian((quint16) d.tcp().window(), buf+len); - len +=2; - - // Fill in cksum as 0 for cksum calculation, actual cksum filled later - qToBigEndian((quint16) 0, buf+len); - len +=2; - - qToBigEndian((quint16) d.tcp().urg_ptr(), buf+len); - len +=2; - - // Accumulate cumulative cksum - cumCksum += ipv4CksumPartial(buf + tcpOfs, len - tcpOfs); - - break; - } - case OstProto::StreamCore::e_l4_udp: - { - udpOfs = len; - - cumCksum = pseudoHdrCksumPartial(srcIp, dstIp, 17, pktLen - len); - - qToBigEndian((quint16) d.udp().src_port(), buf+len); - len += 2; - qToBigEndian((quint16) d.udp().dst_port(), buf+len); - len += 2; - - if (d.udp().is_override_totlen()) - qToBigEndian((quint16) d.udp().totlen(), buf+len); - else - qToBigEndian((quint16) (pktLen - udpOfs), buf+len); - len +=2; - - // Fill in cksum as 0 for cksum calculation, actual cksum filled later - qToBigEndian((quint16) 0, buf+len); - len +=2; - - // Accumulate cumulative cksum - cumCksum += ipv4CksumPartial(buf + udpOfs, len - udpOfs); - - break; - } - case OstProto::StreamCore::e_l4_icmp: - // TODO(LOW) - break; - case OstProto::StreamCore::e_l4_igmp: - // TODO(LOW) - break; - default: - qWarning("Unhandled l4 proto %d\n", d.core().l4_proto()); - } - - // Fill-in the data pattern - dataLen = pktLen - len; - switch(d.core().pattern_mode()) - { - case OstProto::StreamCore::e_dp_fixed_word: - for (int i = 0; i < (dataLen/4)+1; i++) - qToBigEndian((quint32) d.core().pattern(), buf+len+(i*4)); - break; - case OstProto::StreamCore::e_dp_inc_byte: - for (int i = 0; i < dataLen; i++) - buf[len + i] = i % (0xFF + 1); - break; - case OstProto::StreamCore::e_dp_dec_byte: - for (int i = 0; i < dataLen; i++) - buf[len + i] = 0xFF - (i % (0xFF + 1)); - break; - case OstProto::StreamCore::e_dp_random: - for (int i = 0; i < dataLen; i++) - buf[len + i] = qrand() % (0xFF + 1); - break; - default: - qWarning("Unhandled data pattern %d", d.core().pattern_mode()); - } -#endif - -#if 0 // Proto FW - // Calculate TCP/UDP checksum over the data pattern/payload and fill in - switch (d.core().l4_proto()) - { - case OstProto::StreamCore::e_l4_tcp: - if (d.tcp().is_override_cksum()) - qToBigEndian((quint16) d.tcp().cksum(), buf + tcpOfs + 16); - else - *((quint16*)(buf + tcpOfs + 16)) = - ipv4Cksum(buf + len, dataLen, cumCksum); - break; - case OstProto::StreamCore::e_l4_udp: - if (d.udp().is_override_cksum()) - qToBigEndian((quint16) d.udp().cksum(), buf + udpOfs + 6); - else - *((quint16*)(buf + udpOfs + 6)) = - ipv4Cksum(buf + len, dataLen, cumCksum); - break; - case OstProto::StreamCore::e_l4_none: - case OstProto::StreamCore::e_l4_icmp: - case OstProto::StreamCore::e_l4_igmp: - // No cksum processing required - break; - } - return pktLen; -#endif - } @@ -589,28 +201,28 @@ void PortInfo::update() for (int i = 0; i < streamList.size(); i++) { //_restart: - if (streamList[i]->mCore->is_enabled()) + if (streamList[i]->isEnabled()) { long numPackets, numBursts; long ibg, ipg; - switch (streamList[i]->mControl->unit()) + switch (streamList[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: - numBursts = streamList[i]->mControl->num_bursts(); - numPackets = streamList[i]->mControl->packets_per_burst(); - ibg = 1000000/streamList[i]->mControl->bursts_per_sec(); + numBursts = streamList[i]->numBursts(); + numPackets = streamList[i]->burstSize(); + ibg = 1000000/streamList[i]->burstRate(); ipg = 0; break; case OstProto::StreamControl::e_su_packets: numBursts = 1; - numPackets = streamList[i]->mControl->num_packets(); + numPackets = streamList[i]->numPackets(); ibg = 0; - ipg = 1000000/streamList[i]->mControl->packets_per_sec(); + ipg = 1000000/streamList[i]->packetRate(); break; default: qWarning("Unhandled stream control unit %d", - streamList[i]->mControl->unit()); + streamList[i]->sendUnit()); continue; } qDebug("numBursts = %ld, numPackets = %ld\n", @@ -672,7 +284,7 @@ void PortInfo::update() } } // for (numBursts) - switch(streamList[i]->mControl->next()) + switch(streamList[i]->nextWhat()) { case ::OstProto::StreamControl::e_nw_stop: goto _stop_no_more_pkts; @@ -697,7 +309,7 @@ void PortInfo::update() default: qFatal("---------- %s: Unhandled case (%d) -----------", - __FUNCTION__, streamList[i]->mControl->next() ); + __FUNCTION__, streamList[i]->nextWhat() ); break; } From 4c2df3c5a7c794a8b3a6fcd0d7bb79fcbaeb0b20 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 23 Sep 2009 14:53:26 +0000 Subject: [PATCH 024/294] Protocols --------- - all protocols on allocation of a configWidget, also populate it before returning from configWidget() - VLAN TPID override is now correctly saved and restored from/to its widget (vlan.cpp) - Payload protocol returns a minimum frame value of 1 byte size (Hack to avoid crash in stream config dialog when sum of all protocol frame sizes is greater than the frame length - small layout changes in mac widget (mac.ui) - src/dst ip mask changed from 255.255.255.255 to 255.255.255.0 (ip4.proto) - src mac changed from u32 to u64 (mac.proto) - "Combo Protocol" protocol container introduced to define newer protocols as a combination of existing protocols e.g. dot2 = dot3 + llc - THIS IS NOT YET COMPLETE (comboprotocol.h) Client/StreamConfigDialog ------------------------- - Advanced Protocol Selection implemented - Simple Protocol Selection rewritten to work alongside Advanced - Payload Widget is treated like any other protocol - hence it is not placedinto the dialog specially - Any protocol selection change (in Simple/Advanced mode) immediately triggers change in the Stream's protocolList - Protocol Widgets now are arranged in a toolBox on a top level tab of the dialog instead of a nested tabWidget - Vlan selection (Simple Mode) uses Radio buttons instead of checkboxes - Double Tagged (SVlan + CVlan) now works via Simple Mode --- client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/ostinato.qrc | 4 + client/streamconfigdialog.cpp | 1469 +++++++++++++++++++++------------ client/streamconfigdialog.h | 40 +- client/streamconfigdialog.ui | 821 ++++++++++-------- common/comboprotocol.h | 133 +++ common/dot3.cpp | 3 + common/eth2.cpp | 3 + common/ip4.cpp | 3 + common/ip4.proto | 4 +- common/llc.cpp | 3 + common/mac.cpp | 3 + common/mac.proto | 2 +- common/mac.ui | 335 ++++---- common/ostproto.pro | 1 + common/payload.cpp | 8 + common/sample.cpp | 5 +- common/snap.cpp | 3 + common/tcp.cpp | 3 + common/udp.cpp | 3 + common/vlan.cpp | 4 + 24 files changed, 1782 insertions(+), 1068 deletions(-) create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/delete.png create mode 100644 common/comboprotocol.h diff --git a/client/icons/arrow_down.png b/client/icons/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4e279377bf348f9cf53894e76bb673ccf067bd GIT binary patch literal 379 zcmV->0fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 7b735f7..48ab66a 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -1,5 +1,8 @@ + icons/arrow_down.png + icons/arrow_right.png + icons/arrow_up.png icons/bullet_error.png icons/bullet_green.png icons/bullet_orange.png @@ -7,6 +10,7 @@ icons/bullet_yellow.png icons/control_play.png icons/control_stop.png + icons/delete.png icons/magnifier.png icons/portgroup_add.png icons/portgroup_connect.png diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 0c5fc73..98bc612 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -12,7 +12,6 @@ extern ProtocolManager OstProtocolManager; int StreamConfigDialog::lastTopLevelTabIndex = 0; -int StreamConfigDialog::lastProtoTabIndex = 0; StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) @@ -23,17 +22,22 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); mpStream->protoDataCopyFrom(s); _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; setupUi(this); setupUiExtra(); connect(bgFrameType, SIGNAL(buttonClicked(int)), - this, SLOT(updateContents())); + this, SLOT(updateFrameTypeProtocol(int))); + connect(bgVlan, SIGNAL(buttonClicked(int)), + this, SLOT(updateVlanProtocol(int))); connect(bgL3Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateContents())); + this, SLOT(updateL3Protocol(int))); connect(bgL4Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateContents())); - + this, SLOT(updateL4Protocol(int))); + connect(bgPayloadProto, SIGNAL(buttonClicked(int)), + this, SLOT(updatePayloadProtocol(int))); + //! \todo causes a crash! #if 0 connect(lePktLen, SIGNAL(textEdited(QString)), @@ -44,8 +48,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Enable VLAN Choices only if FT = Eth2 or SNAP connect(rbFtNone, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); - - connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); + connect(rbFtNone, SIGNAL(clicked(bool)), rbVlanNone, SLOT(click())); + connect(rbFtNone, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); // Enable/Disable L3 Protocol Choices for FT None connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); @@ -58,7 +63,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); // Force L3 = None if FT = 802.3 Raw - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + connect(rbFt802Dot3Raw, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); // Enable/Disable L3 Protocol Choices for FT 802Dot3Raw connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); @@ -66,7 +71,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); // Force L3 = None if FT = 802.3 LLC (to ensure a valid L3 is selected) - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setChecked(bool))); + connect(rbFt802Dot3Llc, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); // Enable/Disable L3 Protocol Choices for FT 802Dot3Llc connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); @@ -78,6 +83,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); + // Force L3 = Other if FT = Other + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + // Enable/Disable L4 Protocol Choices for L3 Protocol None connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); connect(rbL3None, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); @@ -86,7 +95,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL3None, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); // Force L4 Protocol = None if L3 Protocol is set to None - connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); + connect(rbL3None, SIGNAL(clicked(bool)), rbL4None, SLOT(click())); // Enable/Disable L4 Protocol Choices for L3 Protocol IPv4 connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); @@ -103,11 +112,31 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); // Force L4 Protocol = None if L3 Protocol is set to ARP - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setChecked(bool))); + connect(rbL3Arp, SIGNAL(clicked(bool)), rbL4None, SLOT(click())); + + // Force L4 = Other if L3 = Other + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + + // Force Payload = Other if L4 = Other + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager.protocolDatabase(), this); lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); LoadCurrentStream(); mpPacketModel = new PacketModel(this); @@ -119,8 +148,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // TODO(MED): - //! \todo Implement then enable these protocols - SVLAN, ARP, ICMP, IGMP - cbSVlan->setHidden(true); + //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP rbL3Arp->setHidden(true); rbL3Ipv6->setHidden(true); rbL4Icmp->setHidden(true); @@ -136,8 +164,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Finally, restore the saved last selected tab for the various tab widgets twTopLevel->setCurrentIndex(lastTopLevelTabIndex); - if (twProto->isTabEnabled(lastProtoTabIndex)) - twProto->setCurrentIndex(lastProtoTabIndex); } void StreamConfigDialog::setupUiExtra() @@ -146,53 +172,66 @@ void StreamConfigDialog::setupUiExtra() QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); -#if 0 // MPS - temp mask - // Add the Payload widget to the dialog - { - QGridLayout *layout; - - layout = static_cast(twTopLevel->widget(0)->layout()); - - layout->addWidget(mpStream->protocol(52)->configWidget(), 0, 1); - qDebug("setupUi wgt = %p", mpStream->protocol(52)->configWidget()); - } -#endif - // ---- Setup default stuff that cannot be done in designer ---- gbVlan->setDisabled(true); bgFrameType = new QButtonGroup(); +#if 0 foreach(QRadioButton *btn, gbFrameType->findChildren()) bgFrameType->addButton(btn); +#else + bgFrameType->addButton(rbFtNone, 0); + bgFrameType->addButton(rbFtEthernet2, 121); + bgFrameType->addButton(rbFt802Dot3Raw, 122); + bgFrameType->addButton(rbFt802Dot3Llc, 123); + bgFrameType->addButton(rbFtLlcSnap, 124); + bgFrameType->addButton(rbFtOther, -1); +#endif + + bgVlan = new QButtonGroup(); + bgVlan->addButton(rbVlanNone, 0); + bgVlan->addButton(rbVlanSingle, 126); + bgVlan->addButton(rbVlanDouble, 127); bgL3Proto = new QButtonGroup(); +#if 0 foreach(QRadioButton *btn, gbL3Proto->findChildren()) bgL3Proto->addButton(btn); +#else + bgL3Proto->addButton(rbL3None, 0); + bgL3Proto->addButton(rbL3Ipv4, 130); + bgL3Proto->addButton(rbL3Ipv6, -1); + bgL3Proto->addButton(rbL3Arp, -1); + bgL3Proto->addButton(rbL3Other, -1); +#endif bgL4Proto = new QButtonGroup(); +#if 0 foreach(QRadioButton *btn, gbL4Proto->findChildren()) bgL4Proto->addButton(btn); +#else + bgL4Proto->addButton(rbL4None, 0); + bgL4Proto->addButton(rbL4Tcp, 140); + bgL4Proto->addButton(rbL4Udp, 141); + bgL4Proto->addButton(rbL4Icmp, -1); + bgL4Proto->addButton(rbL4Igmp, -1); + bgL4Proto->addButton(rbL4Other, -1); +#endif - //twProto->setTabEnabled(2, FALSE); - //twProto->setTabEnabled(3, FALSE); - + bgPayloadProto = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgPayloadProto->addButton(btn); +#else + bgPayloadProto->addButton(rbPayloadPattern, 52); + bgPayloadProto->addButton(rbPayloadOther, -1); +#endif /* ** Setup Validators */ // Meta Data //! \todo - doesn't seem to work - range validator needs a spinbox? //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); - - // L2 Ethernet -#if 0 // Proto FW - leDstMac->setValidator(new QRegExpValidator(reMac, this)); - leSrcMac->setValidator(new QRegExpValidator(reMac, this)); - leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); - leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); - leCvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); - leSvlanTpid->setValidator(new QRegExpValidator(reHex2B, this)); - //leEtherType->setValidator(new QRegExpValidator(reHex2B, this)); -#endif /* ** Setup Connections @@ -213,138 +252,16 @@ StreamConfigDialog::~StreamConfigDialog() delete mpPacketModelTester; delete mpPacketModel; -#if 0 // MPS - temp mask - // Remove payload data widget so that it is not deleted when this object - // is destroyed - { - QLayout *layout = twTopLevel->widget(0)->layout(); - if (layout) - { - layout->removeWidget(mpStream->protocol(52)->configWidget()); - mpStream->protocol(52)->configWidget()->setParent(0); - } - } -#endif - - // Remove any existing widget on the L2-L4 Tabs lest they are deleted - // when this object is destoryed - for (int i = 1; i <= 3; i++) - { - QLayout *layout = twProto->widget(i)->layout(); - if (layout) - { - QLayoutItem *child; - while ((child = layout->takeAt(0)) != 0) - { - Q_ASSERT(child->widget() != 0); - // Don't delete the child widget - reparent it - child->widget()->setParent(0); - } - delete layout; - } - } - delete bgFrameType; + delete bgVlan; delete bgL3Proto; delete bgL4Proto; + delete bgPayloadProto; delete _iter; delete mpStream; } -void StreamConfigDialog::updateSelectedProtocols() -{ -#define CHKINS(p) \ -{ \ - if (_iter->hasNext() && (_iter->peekNext()->protocolNumber() == OstProto::Protocol::k##p##FieldNumber)) \ - _iter->next(); \ - else \ - _iter->insert(OstProtocolManager.createProtocol(OstProto::Protocol::k##p##FieldNumber, mpStream)); \ -} - - _iter->toFront(); -#if 1 - qDebug("Before Update"); - while (_iter->hasNext()) - { - AbstractProtocol* p; - - p = _iter->next(); - qDebug("%p:[%d]", p, p->protocolNumber()); - } - - _iter->toFront(); -#endif - - // Mac - CHKINS(Mac) - - if (cbCVlan->isEnabled() && cbCVlan->isChecked()) - CHKINS(Vlan); - - if (rbFtEthernet2->isChecked()) - CHKINS(Eth2) - else if (rbFt802Dot3Raw->isChecked()) - CHKINS(Dot3) - else if (rbFt802Dot3Llc->isChecked()) - { - CHKINS(Dot3); - CHKINS(Llc); - } - else if (rbFtLlcSnap->isChecked()) - { - CHKINS(Dot3); - CHKINS(Llc); - CHKINS(Snap); - } - - if (rbL3Ipv4->isChecked()) - CHKINS(Ip4) - else if (rbL3Arp->isChecked()) - CHKINS(Arp) - - if (rbL4Tcp->isChecked()) - CHKINS(Tcp) - else if (rbL4Udp->isChecked()) - CHKINS(Udp) - else if (rbL4Icmp->isChecked()) - CHKINS(Icmp) - else if (rbL4Igmp->isChecked()) - CHKINS(Igmp) - - // Payload - CHKINS(Payload) - - // Remove all protocols, if any, beyond payload - while (_iter->hasNext()) - { - _iter->next(); - _iter->remove(); - } - - -#if 1 - qDebug("After Update"); - _iter->toFront(); - while (_iter->hasNext()) - { - AbstractProtocol* p; - - p = _iter->next(); - qDebug("%p:[%d]", p, p->protocolNumber()); - } -#endif - -#undef CHKINS - -} - -void StreamConfigDialog::updateContents() -{ - StoreCurrentStream(); - LoadCurrentStream(); -} - void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { if (mode == "Fixed") @@ -401,152 +318,232 @@ void StreamConfigDialog::on_pbNext_clicked() #endif } -void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) { - // We only process the "Packet View" tab - if (index != 2) - return; - - updateContents(); - mpPacketModel->setSelectedProtocols(*_iter); + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } } -void StreamConfigDialog::on_twProto_currentChanged(int index) +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected) { -#if 0 // MPS - temp mask - QLayout *layout; - QList wl; + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); - // We need to process only indices 1-3 i.e. the L2, L3 and L4 tabs - if ((index < 1) || (index > 3)) + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) return; - // Remove any existing widget on the activated tab - layout = twProto->widget(index)->layout(); - if (layout) - { - QLayoutItem *child; - while ((child = layout->takeAt(0)) != 0) - { - Q_ASSERT(child->widget() != 0); - // Don't delete the child widget - reparent it - child->widget()->setParent(0); - } - delete layout; - } - - // FIXME: protocol id hardcodings - switch(index) - { - case 1: // L2 - wl.append(mpStream->protocol("mac")->configWidget()); - if (cbCVlan->isEnabled() && cbCVlan->isChecked()) - wl.append(mpStream->protocol("vlan")->configWidget()); - if (rbFtEthernet2->isChecked()) - wl.append(mpStream->protocol("eth2")->configWidget()); - else if (rbFt802Dot3Raw->isChecked()) - wl.append(mpStream->protocol("dot3")->configWidget()); - else if (rbFt802Dot3Llc->isChecked()) - { - wl.append(mpStream->protocol("dot3")->configWidget()); - wl.append(mpStream->protocol("llc")->configWidget()); - } - else if (rbFtLlcSnap->isChecked()) - { - wl.append(mpStream->protocol("dot3")->configWidget()); - wl.append(mpStream->protocol("llc")->configWidget()); - wl.append(mpStream->protocol("snap")->configWidget()); - } - - { - int i, r = 0, c = 0; - QGridLayout *layout = new QGridLayout; - - Q_ASSERT(wl.size() > 0); - - // We use a 2 column layout for the L2 Tab - - layout->addWidget(wl.at(0), r, c, 1, -1); - r++; - for (i=1; i < wl.size(); i++) - { - layout->addWidget(wl.at(i), r, c); - if ((i % 2) == 0) - r++; - c = (c+1) % 2; - - } - if ((i % 2) == 0) - r++; - layout->setRowStretch(r, 10); - - twProto->widget(index)->setLayout(layout); - } - break; - case 2: // L3 - if (rbL3Ipv4->isChecked()) - wl.append(mpStream->protocol("ip4")->configWidget()); - - if (wl.size()) - { - QVBoxLayout *layout = new QVBoxLayout; - - for (int i=0; i < wl.size(); i++) - layout->addWidget(wl.at(i)); - - twProto->widget(index)->setLayout(layout); - } - break; - case 3: // L4 - if (rbL4Tcp->isChecked()) - wl.append(mpStream->protocol("tcp")->configWidget()); - else if (rbL4Udp->isChecked()) - wl.append(mpStream->protocol("udp")->configWidget()); - - if (wl.size()) - { - QVBoxLayout *layout = new QVBoxLayout; - - for (int i=0; i < wl.size(); i++) - layout->addWidget(wl.at(i)); - - twProto->widget(index)->setLayout(layout); - } - break; - } -#else - int selTab; - - qDebug("In %s (tab index = %d)", __FUNCTION__, index); - // We need to process only index 4 i.e. the Protocol Data tab - if (index != 4) - return; - - // Hide the ToolBox before modifying it - otherwise we have a crash !!! - tbProtocolData->hide(); - - selTab = tbProtocolData->currentIndex(); - - // Remove any existing widget on the activated tab - while (tbProtocolData->count() > 0) - { - QWidget* w = tbProtocolData->widget(0); - tbProtocolData->removeItem(0); - w->setParent(0); - } + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); _iter->toFront(); - while (_iter->hasNext()) + while (n--) { - AbstractProtocol* p = _iter->next(); - tbProtocolData->addItem(p->configWidget(), p->name()); + if (!_iter->hasNext()) + return; + + p = _iter->next(); } - if (selTab < tbProtocolData->count()) - tbProtocolData->setCurrentIndex(selTab); + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager.createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); - tbProtocolData->show(); -#endif + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + QWidget *selWidget; + + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + selWidget = tbProtocolData->currentWidget(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + tbProtocolData->setCurrentWidget(selWidget); + + tbProtocolData->show(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } } void StreamConfigDialog::update_NumPacketsAndNumBursts() @@ -576,6 +573,635 @@ void StreamConfigDialog::on_lePattern_editingFinished() } #endif +bool StreamConfigDialog::skipProtocols(int layer) +{ +#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) + + _iter->toFront(); + + // Check and skip 'mac' + if (CHK(51)) + _iter->next(); + else + goto _unexpected_proto; + + if (layer == 0) + goto _done; + + // Skip VLANs + while (CHK(126)) + _iter->next(); + + if (layer == 1) + goto _done; + + // Skip L2 (FrameType) + if (CHK(121)) // Eth2 + _iter->next(); + else if (CHK(122)) // 802.3 RAW + { + _iter->next(); + if (CHK(123)) // 802.3 LLC + { + _iter->next(); + if (CHK(124)) // SNAP + _iter->next(); + } + } + else + goto _unexpected_proto; + + if (layer == 2) + goto _done; + + // Skip L3 + if (CHK(130)) // IP4 + _iter->next(); + else if (CHK(131)) // ARP + _iter->next(); + else + goto _unexpected_proto; + + if (layer == 3) + goto _done; + + // Skip L4 + if (CHK(140)) // TCP + _iter->next(); + else if (CHK(141)) // UDP + _iter->next(); + else if (CHK(142)) // ICMP + _iter->next(); + else if (CHK(143)) // IGMP + _iter->next(); + else + goto _unexpected_proto; + + if (layer == 4) + goto _done; + + goto _unexpected_proto; + +_done: + return true; + +_unexpected_proto: + qWarning("%s: unexpected protocol", __FUNCTION__); + return false; + +#undef CHK +} + +void StreamConfigDialog::updateFrameTypeProtocol(int newId) +{ + static int oldId = 0; + AbstractProtocol *p; + + qDebug("%s:old id = %d new id = %d, upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + Q_ASSERT(newId != -1); + + if (!skipProtocols(1)) + goto _error; + + // Delete old Id + switch (oldId) + { + case 0: + break; + case -1: + // Blindly remove the current protocol + p =_iter->next(); + _iter->remove(); + delete p; + break; + + case 121: // ethernet + case 122: // dot3 + p =_iter->next(); + if (p->protocolNumber() != (quint32) oldId) + goto _error; + + _iter->remove(); + delete p; + break; + case 123: // dot3 llc + p =_iter->next(); + if (p->protocolNumber() != 122) + goto _error; + _iter->remove(); + delete p; + + p =_iter->next(); + if (p->protocolNumber() != 123) + goto _error; + _iter->remove(); + delete p; + break; + case 124: // dot3 llc snap + p =_iter->next(); + if (p->protocolNumber() != 122) + goto _error; + _iter->remove(); + delete p; + + p =_iter->next(); + if (p->protocolNumber() != 123) + goto _error; + _iter->remove(); + delete p; + + p =_iter->next(); + if (p->protocolNumber() != 124) + goto _error; + _iter->remove(); + delete p; + break; + default: + goto _error; + } + + // Insert new Id + switch (newId) + { + case 0: + break; + case 121: // ethernet + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + case 122: // dot3 + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + case 123: // dot3 llc + _iter->insert(OstProtocolManager.createProtocol( + 122, mpStream)); + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + case 124: // dot3 llc snap + _iter->insert(OstProtocolManager.createProtocol( + 122, mpStream)); + _iter->insert(OstProtocolManager.createProtocol( + 123, mpStream)); + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + default: + goto _error; + } + } + + oldId = newId; + return; + +_error: + qFatal("%s: unexpected incident", __FUNCTION__); +} + +void StreamConfigDialog::updateVlanProtocol(int newId) +{ + static int oldId = 0; + AbstractProtocol *p; + + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + Q_ASSERT(newId != -1); + + if (!skipProtocols(0)) + goto _error; + + // Delete oldId proto + switch (oldId) + { + case 0: + switch (newId) + { + case 127: + _iter->insert(OstProtocolManager.createProtocol( + 126, mpStream)); + // No 'break'; fallthrough - by design! + case 126: + _iter->insert(OstProtocolManager.createProtocol( + 126, mpStream)); + break; + default: + goto _error; + } + break; + + case 126: + if (!_iter->hasNext()) + goto _error; + p =_iter->next(); + if (p->protocolNumber() != 126) + goto _error; + switch (newId) + { + case 0: + _iter->remove(); + delete p; + break; + + case 127: + _iter->insert(OstProtocolManager.createProtocol( + 126, mpStream)); + break; + default: + goto _error; + } + break; + + case 127: + if (!_iter->hasNext()) + goto _error; + p =_iter->next(); + if (p->protocolNumber() != 126) + goto _error; + p =_iter->next(); + if (p->protocolNumber() != 126) + goto _error; + switch (newId) + { + case 0: + _iter->previous(); + _iter->previous(); + p =_iter->next(); + _iter->remove(); + delete p; + p =_iter->next(); + _iter->remove(); + delete p; + break; + + case 126: + _iter->previous(); + _iter->previous(); + p =_iter->next(); + _iter->remove(); + delete p; + break; + default: + goto _error; + } + break; + default: + goto _error; + } + } + + oldId = newId; + return; + +_error: + qFatal("%s: unexpected incident", __FUNCTION__); +} + +void StreamConfigDialog::updateL3Protocol(int newId) +{ + static int oldId = 0; + AbstractProtocol *p; + + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + Q_ASSERT(newId != -1); + + if (!skipProtocols(2)) + goto _error; + + switch (oldId) + { + case 0: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + + case -1: + default: + if (!_iter->hasNext()) + goto _error; + + p =_iter->next(); + + if ((oldId != -1) && (p->protocolNumber() == (quint32) newId)) + goto _error; + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + break; + } + } + + oldId = newId; + return; + +_error: + qFatal("%s: unexpected incident", __FUNCTION__); +} + +void StreamConfigDialog::updateL4Protocol(int newId) +{ + static int oldId = 0; + AbstractProtocol *p; + + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + Q_ASSERT(newId != -1); + + if (!skipProtocols(3)) + goto _error; + + switch (oldId) + { + case 0: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + + case -1: + default: + if (!_iter->hasNext()) + goto _error; + + p =_iter->next(); + + if ((oldId != -1) && (p->protocolNumber() == (quint32) newId)) + goto _error; + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + break; + } + } + + oldId = newId; + return; + +_error: + qFatal("%s: unexpected incident", __FUNCTION__); +} + +void StreamConfigDialog::updatePayloadProtocol(int newId) +{ + static int oldId = 52; + AbstractProtocol *p; + + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + Q_ASSERT(newId != 0); + Q_ASSERT(newId != -1); + + if (!skipProtocols(4)) + goto _error; + + if (!_iter->hasNext()) + goto _error; + + p =_iter->next(); + + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + delete p; + while (_iter->hasNext()) + { + + p = _iter->next(); + _iter->remove(); + delete p; + } + } + + oldId = newId; + return; + +_error: + qFatal("%s: unexpected incident", __FUNCTION__); +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ +#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + _iter->toFront(); + + // + // We expect first protocol to be 'mac' usually + // + if (CHK(51)) // Mac + { + _iter->next(); + } + else + { + rbFtOther->setChecked(true); + goto _done; + } + + + // + // Check for "VLAN" + // + if (CHK(126)) // VLAN + { + _iter->next(); + if (CHK(126)) // VLAN + { + rbVlanDouble->setChecked(true); + updateVlanProtocol(127); + _iter->next(); + } + else + { + rbVlanSingle->setChecked(true); + updateVlanProtocol(126); + } + } + else + { + rbVlanNone->setChecked(true); + updateVlanProtocol(0); + } + + + // + // Identify and set "FrameType" + // + if (CHK(52)) // Payload + { + rbFtNone->click(); + goto _payload; + } + else if (CHK(121)) // Eth2 + { + rbFtEthernet2->click(); + _iter->next(); + } + else if (CHK(122)) // 802.3 RAW + { + _iter->next(); + if (CHK(123)) // 802.3 LLC + { + _iter->next(); + if (CHK(124)) // SNAP + { + rbFtLlcSnap->click(); + _iter->next(); + } + else + { + rbFt802Dot3Llc->click(); + } + } + else + { + rbFt802Dot3Raw->click(); + } + } + else + { + rbFtOther->setChecked(true); + goto _done; + } + + + // + // --- L3 --- + // + if (CHK(130)) // IP4 + { + rbL3Ipv4->click(); + _iter->next(); + } + else if (CHK(131)) // ARP + { + rbL3Arp->click(); + _iter->next(); + } + else if (CHK(52)) // Payload + { + rbL3None->click(); + goto _payload; + } + else + { + rbL3Other->setChecked(true); + goto _done; + } + + // + // --- L4 --- + // + if (!_iter->hasNext()) + { + rbL4Other->setChecked(true); + goto _done; + } + else if (CHK(140)) // TCP + { + rbL4Tcp->click(); + _iter->next(); + } + else if (CHK(141)) // UDP + { + rbL4Udp->click(); + _iter->next(); + } + else if (CHK(142)) // ICMP + { + rbL4Icmp->click(); + _iter->next(); + } + else if (CHK(143)) // IGMP + { + rbL4Igmp->click(); + _iter->next(); + } + else if (CHK(52)) // Payload + { + rbL4None->click(); + goto _payload; + } + else + { + rbL4Other->setChecked(true); + goto _done; + } + +_payload: + // + // Payload + // + if (CHK(52)) // Payload + { + rbPayloadPattern->click(); + _iter->next(); + } + + // If there is any protocol beyond "data pattern" ... + if (_iter->hasNext()) + rbPayloadOther->setChecked(true); + +_done: + // If any "other" protocols are checked, QButtonGroup signals + // are not triggered since the "other" radioButton's are disabled + // - so update manually + if (rbFtOther->isChecked()) + updateFrameTypeProtocol(-1); + if (rbL3Other->isChecked()) + updateL3Protocol(-1); + if (rbL4Other->isChecked()) + updateL4Protocol(-1); + if (rbPayloadOther->isChecked()) + updatePayloadProtocol(-1); + isUpdateInProgress = false; + + return; +#undef CHK +} + void StreamConfigDialog::LoadCurrentStream() { QString str; @@ -590,247 +1216,13 @@ void StreamConfigDialog::LoadCurrentStream() lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); } -#if 0 // MPS - temp mask // Protocols { - int i; + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); - qDebug("Selected Protocols.size() = %d", mSelectedProtocols.size()); - mSelectedProtocols = mpStream->frameProtocol(); - qDebug("Selected Protocols.size() = %d", mSelectedProtocols.size()); - for (i = 0; i < mSelectedProtocols.size(); i++) - qDebug("%d: %d", i, mSelectedProtocols.at(i)); - - Q_ASSERT(mSelectedProtocols.size() >= 2); // Mac + Payload: mandatory - - i = 0; - Q_ASSERT(mSelectedProtocols.at(i) == 51); // Mac - i++; - - // VLAN - if (mSelectedProtocols.at(i) == 126) // VLAN - { - cbCVlan->setChecked(true); - i++; - } - - if (mSelectedProtocols.at(i) == 52) // Payload - { - i++; - goto _proto_parse_done; - } - else if (mSelectedProtocols.at(i) == 121) // Eth2 - { - rbFtEthernet2->setChecked(true); - i++; - } - else if (mSelectedProtocols.at(i) == 122) // 802.3 RAW - { - if ((mSelectedProtocols.size() > (i+1)) && - (mSelectedProtocols.at(i+1) == 123)) // 802.3 LLC - { - if ((mSelectedProtocols.size() > (i+2)) && - (mSelectedProtocols.at(i+2) == 124)) // SNAP - { - rbFtLlcSnap->setChecked(true); - i+=3; - } - else - { - rbFt802Dot3Llc->setChecked(true); - i+=2; - } - } - else - { - rbFt802Dot3Raw->setChecked(true); - i++; - } - } - else - rbFtNone->setChecked(true); - - - // L3 - if (mSelectedProtocols.at(i) == 52) // Payload - { - i++; - goto _proto_parse_done; - } - else if (mSelectedProtocols.at(i) == 130) // IP4 - { - rbL3Ipv4->setChecked(true); - i++; - } - else if (mSelectedProtocols.at(i) == 131) // ARP - { - rbL3Arp->setChecked(true); - i++; - } - else - rbL3None->setChecked(true); - - if (i == mSelectedProtocols.size()) - goto _proto_parse_done; - - // L4 - if (mSelectedProtocols.at(i) == 52) // Payload - { - i++; - goto _proto_parse_done; - } - else if (mSelectedProtocols.at(i) == 140) // TCP - { - rbL4Tcp->setChecked(true); - i++; - } - else if (mSelectedProtocols.at(i) == 141) // UDP - { - rbL4Udp->setChecked(true); - i++; - } - else if (mSelectedProtocols.at(i) == 142) // ICMP - { - rbL4Icmp->setChecked(true); - i++; - } - else if (mSelectedProtocols.at(i) == 143) // IGMP - { - rbL4Igmp->setChecked(true); - i++; - } - else - rbL4None->setChecked(true); - - Q_ASSERT(mSelectedProtocols.at(i) == 52); // Payload - i++; - -_proto_parse_done: - Q_ASSERT(i == mSelectedProtocols.size()); mpStream->loadProtocolWidgets(); } -#else - // Protocols - { - qDebug("Loading - current list"); - _iter->toFront(); - while(_iter->hasNext()) - { - AbstractProtocol* p = _iter->next(); - qDebug("%p -- %d", p, p->protocolNumber()); - } - _iter->toFront(); - -#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) - - Q_ASSERT(CHK(51)); // Mac - _iter->next(); - - // VLAN - if (CHK(126)) // VLAN - { - cbCVlan->setChecked(true); - _iter->next(); - } - - if (CHK(52)) // Payload - { - _iter->next(); - goto _proto_parse_done; - } - else if (CHK(121)) // Eth2 - { - rbFtEthernet2->setChecked(true); - _iter->next(); - } - else if (CHK(122)) // 802.3 RAW - { - _iter->next(); - if (CHK(123)) // 802.3 LLC - { - _iter->next(); - if (CHK(124)) // SNAP - { - rbFtLlcSnap->setChecked(true); - _iter->next(); - } - else - { - rbFt802Dot3Llc->setChecked(true); - } - } - else - { - rbFt802Dot3Raw->setChecked(true); - } - } - else - rbFtNone->setChecked(true); - - - // L3 - if (CHK(52)) // Payload - { - _iter->next(); - goto _proto_parse_done; - } - else if (CHK(130)) // IP4 - { - rbL3Ipv4->setChecked(true); - _iter->next(); - } - else if (CHK(131)) // ARP - { - rbL3Arp->setChecked(true); - _iter->next(); - } - else - rbL3None->setChecked(true); - - if (!_iter->hasNext()) - goto _proto_parse_done; - - // L4 - if (CHK(52)) // Payload - { - _iter->next(); - goto _proto_parse_done; - } - else if (CHK(140)) // TCP - { - rbL4Tcp->setChecked(true); - _iter->next(); - } - else if (CHK(141)) // UDP - { - rbL4Udp->setChecked(true); - _iter->next(); - } - else if (CHK(142)) // ICMP - { - rbL4Icmp->setChecked(true); - _iter->next(); - } - else if (CHK(143)) // IGMP - { - rbL4Igmp->setChecked(true); - _iter->next(); - } - else - rbL4None->setChecked(true); - - Q_ASSERT(CHK(52)); // Payload - _iter->next(); - -_proto_parse_done: - Q_ASSERT(!_iter->hasNext()); - mpStream->loadProtocolWidgets(); - -#undef CHK - } - - -#endif // Stream Control { @@ -900,8 +1292,6 @@ void StreamConfigDialog::StoreCurrentStream() // Protocols { - updateSelectedProtocols(); - //pStream->setFrameProtocol(mSelectedProtocols); pStream->storeProtocolWidgets(); } @@ -937,13 +1327,14 @@ void StreamConfigDialog::on_pbOk_clicked() OstProto::Stream s; // Store dialog contents into stream - //updateSelectedProtocols(); StoreCurrentStream(); + + // Copy the data from the "local working copy of stream" to "actual stream" mpStream->protoDataCopyInto(s); - mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s);; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + qDebug("stream stored"); lastTopLevelTabIndex = twTopLevel->currentIndex(); - lastProtoTabIndex = twProto->currentIndex(); } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index c0e02a7..4b774f7 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -29,10 +29,13 @@ public: private: QButtonGroup *bgFrameType; + QButtonGroup *bgVlan; QButtonGroup *bgL3Proto; QButtonGroup *bgL4Proto; + QButtonGroup *bgPayloadProto; QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; Port& mPort; uint mCurrentStreamIndex; @@ -40,6 +43,8 @@ private: Stream *mpStream; ProtocolListIterator *_iter; + bool isUpdateInProgress; + PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; @@ -47,7 +52,6 @@ private: // for the various tab widgets so that it can be restored when the dialog // is opened the next time static int lastTopLevelTabIndex; - static int lastProtoTabIndex; void setupUiExtra(); void updateSelectedProtocols(); @@ -56,15 +60,37 @@ private: private slots: void on_cmbPktLenMode_currentIndexChanged(QString mode); - void on_pbPrev_clicked(); - void on_pbNext_clicked(); - - void updateContents(); + void update_NumPacketsAndNumBursts(); void on_twTopLevel_currentChanged(int index); - void on_twProto_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); - void update_NumPacketsAndNumBursts(); + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void updateFrameTypeProtocol(int id); + void updateVlanProtocol(int id); + void updateL3Protocol(int id); + void updateL4Protocol(int id); + void updatePayloadProtocol(int id); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); void on_pbOk_clicked(); }; diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 198cbf2..6e15d3b 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,8 +8,8 @@ 0 0 - 636 - 589 + 524 + 458 @@ -35,8 +35,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff true - - + + @@ -46,7 +46,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - Packet Config + Protocol Selection @@ -56,7 +56,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 171 + 241 20 @@ -164,237 +164,366 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + - 4 + 0 - - - Protocols + + + + 0 + 0 + 465 + 226 + + + + Simple + + + + + + Frame Type + + + + + + None (Mac Only) + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + None + + + true + + + + + + + Single Tag + + + + + + + Double Tag + + + + + + + + + + L3 + + + + + + None + + + true + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + ARP + + + + + + + false + + + Other + + + + + + + + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + + + + Payload + + + + + + Pattern + + + true + + + + + + + false + + + Other + + + + + + + + + + Qt::Vertical + + + + 107 + 24 + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced - - - Frame Type + + + Available Protocols - - - - - None - - - true - - - - - - - Ethernet II - - - false - - - - - - - 802.3 Raw - - - - - - - 802.3 LLC - - - false - - - - - - - LLC SNAP - - - - - - + + true - - VLAN + + QAbstractItemView::ExtendedSelection - - false + + QAbstractItemView::SelectRows - - false - - - - - - true - - - CVLAN - - - - - - - true - - - SVLAN - - - - + + + + - - - L3 + + + Qt::Vertical - - - - - None - - - true - - - - - - - false - - - IPv4 - - - false - - - - - - - false - - - IPv6 - - - - - - - false - - - ARP - - - - - - - false - - - Other - - - - - + + + 20 + 40 + + + - - - L4 + + + false + + + > + + + :/icons/arrow_right.png - - - - - None - - - true - - - - - - - false - - - ICMP - - - - - - - false - - - IGMP - - - - - - - false - - - TCP - - - - - - - false - - - UDP - - - - - - - false - - - Other - - - - @@ -404,8 +533,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 182 - 16 + 20 + 40 @@ -413,126 +542,78 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - Advanced Protocol Selection - - - true - - - false - - - - - - - - - - Available Protocols - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - > - - - - - - - < - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Selected Protocols - - - - - - - - - - - - - - - - - - L2 - - - - - - L3 - - - - - L4 - - - - - - Protocol Data - - - - - - -1 - - + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + @@ -540,6 +621,20 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + Protocol Data + + + + + + -1 + + + + + Stream Control @@ -925,7 +1020,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + @@ -989,7 +1084,55 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff lePktLen lePktLenMin lePktLenMax - twProto + rbFtNone + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..62828cb --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,133 @@ +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +private: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream) + { + protoA = new ProtoA(stream); + protoB = new ProtoB(stream); + configForm = NULL; + } + virtual ~ComboProtocol() + { + delete protoA; + delete protoB; + delete configForm; + } + + static ComboProtocol* createInstance(StreamBase *stream) + { + return new ComboProtocol(stream); + } + + virtual quint32 protocolNumber() const + { + return 0; //FIXME + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + // FIXME + } + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + // FIXME + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + if (index < protoA->fieldCount()) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + if (index < protoA->fieldCount()) + return protoA->fieldData(index); + else + return protoB->fieldData(index); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + if (index < protoA->fieldCount()) + return protoA->fieldData(index); + else + return protoB->fieldData(index); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new VBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/dot3.cpp b/common/dot3.cpp index be6d580..f020b6e 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -118,7 +118,10 @@ bool Dot3Protocol::setFieldData(int index, const QVariant &value, QWidget* Dot3Protocol::configWidget() { if (configForm == NULL) + { configForm = new Dot3ConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/eth2.cpp b/common/eth2.cpp index 1d72042..d1c4b9c 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -121,7 +121,10 @@ bool Eth2Protocol::setFieldData(int index, const QVariant &value, QWidget* Eth2Protocol::configWidget() { if (configForm == NULL) + { configForm = new Eth2ConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/ip4.cpp b/common/ip4.cpp index 653c83f..296f09b 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -604,7 +604,10 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, QWidget* Ip4Protocol::configWidget() { if (configForm == NULL) + { configForm = new Ip4ConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/ip4.proto b/common/ip4.proto index 6889ffd..c17ec75 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -31,13 +31,13 @@ message Ip4 { optional fixed32 src_ip = 14; optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; optional uint32 src_ip_count = 16 [default = 16]; - optional fixed32 src_ip_mask = 17 [default = 0xFFFFFFFF]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; // Destination IP optional fixed32 dst_ip = 18; optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; optional uint32 dst_ip_count = 20 [default = 16]; - optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFFFF]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; // TODO: Options } diff --git a/common/llc.cpp b/common/llc.cpp index 150b8b8..8fe57af 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -135,7 +135,10 @@ bool LlcProtocol::setFieldData(int index, const QVariant &value, QWidget* LlcProtocol::configWidget() { if (configForm == NULL) + { configForm = new LlcConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/mac.cpp b/common/mac.cpp index dfe3ed8..7f87a5b 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -243,7 +243,10 @@ bool MacProtocol::setFieldData(int index, const QVariant &value, QWidget* MacProtocol::configWidget() { if (configForm == NULL) + { configForm = new MacConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/mac.proto b/common/mac.proto index 2e8c04e..a49c34e 100644 --- a/common/mac.proto +++ b/common/mac.proto @@ -18,7 +18,7 @@ message Mac { optional uint32 dst_mac_step = 4 [default = 1]; // Src Mac - optional uint32 src_mac = 5; + optional uint64 src_mac = 5; optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; optional uint32 src_mac_count = 7 [default = 16]; optional uint32 src_mac_step = 8 [default = 1]; diff --git a/common/mac.ui b/common/mac.ui index 78a2c74..821cf00 100644 --- a/common/mac.ui +++ b/common/mac.ui @@ -5,193 +5,170 @@ 0 0 - 512 - 104 + 391 + 116 Form - - - - - MAC + + + + + Address - - - - - Destination - - - - - - - - 120 - 0 - - - - >HH HH HH HH HH HH; - - - - - - - - - - Mode - - - - - - - - Fixed - - - - - Increment - - - - - Decrement - - - - - - - - Count - - - - - - - false - - - - - - 0 - - - - - - - Step - - - - - - - false - - - - - - 0 - - - - - - - Source - - - - - - - >HH HH HH HH HH HH; - - - - - - - - - - Mode - - - - - - - - Fixed - - - - - Increment - - - - - Decrement - - - - - - - - Count - - - - - - - false - - - - - - - - - - Step - - - - - - - false - - - - - - 0 - - - - - + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + Qt::Vertical diff --git a/common/ostproto.pro b/common/ostproto.pro index 6095613..0e095a1 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -28,6 +28,7 @@ PROTOS += \ udp.proto HEADERS += \ abstractprotocol.h \ + comboprotocol.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ diff --git a/common/payload.cpp b/common/payload.cpp index 5633ed6..6c93cb3 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -126,6 +126,11 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, dataLen = mpStream->frameLen() - protocolFrameOffset(); dataLen -= SZ_FCS; + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + fv.resize(dataLen+4); switch(data.pattern_mode()) { @@ -180,7 +185,10 @@ bool PayloadProtocol::setFieldData(int index, const QVariant &value, QWidget* PayloadProtocol::configWidget() { if (configForm == NULL) + { configForm = new PayloadConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/sample.cpp b/common/sample.cpp index 17ff0c3..9dbb81e 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -155,7 +155,10 @@ bool SampleProtocol::setFieldData(int index, const QVariant &value, QWidget* SampleProtocol::configWidget() { if (configForm == NULL) - configFrom = new SampleConfigForm; + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/snap.cpp b/common/snap.cpp index 0aca641..26f5c6c 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -140,7 +140,10 @@ bool SnapProtocol::setFieldData(int index, const QVariant &value, QWidget* SnapProtocol::configWidget() { if (configForm == NULL) + { configForm = new SnapConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/tcp.cpp b/common/tcp.cpp index c3b732b..279efc1 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -384,7 +384,10 @@ bool TcpProtocol::setFieldData(int index, const QVariant &value, QWidget* TcpProtocol::configWidget() { if (configForm == NULL) + { configForm = new TcpConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/udp.cpp b/common/udp.cpp index 9baae78..7564d2a 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -261,7 +261,10 @@ bool UdpProtocol::setFieldData(int index, const QVariant &value, QWidget* UdpProtocol::configWidget() { if (configForm == NULL) + { configForm = new UdpConfigForm; + loadConfigWidget(); + } return configForm; } diff --git a/common/vlan.cpp b/common/vlan.cpp index f261264..2f60601 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -203,7 +203,10 @@ bool VlanProtocol::setFieldData(int index, const QVariant &value, QWidget* VlanProtocol::configWidget() { if (configForm == NULL) + { configForm = new VlanConfigForm; + loadConfigWidget(); + } return configForm; } @@ -211,6 +214,7 @@ void VlanProtocol::loadConfigWidget() { configWidget(); + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); From a937112e6325cf18e018be408848c916ad6aedc8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 11 Oct 2009 06:07:27 +0000 Subject: [PATCH 025/294] Checking in intermediate changes in Stream Config Dialog. Changes to related to handling of protocols after introducing combo protocol --- client/streamconfigdialog.cpp | 790 +++++++++++++++------------------- 1 file changed, 344 insertions(+), 446 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 98bc612..9bdae53 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -27,7 +27,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, setupUi(this); setupUiExtra(); - connect(bgFrameType, SIGNAL(buttonClicked(int)), + connect(bgL1Proto, SIGNAL(buttonClicked(int)), + this, SLOT(updateL1Protocol(int))); + connect(bgL2Proto, SIGNAL(buttonClicked(int)), this, SLOT(updateFrameTypeProtocol(int))); connect(bgVlan, SIGNAL(buttonClicked(int)), this, SLOT(updateVlanProtocol(int))); @@ -47,9 +49,17 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Time to play match the signals and slots! // Enable VLAN Choices only if FT = Eth2 or SNAP +#if 0 connect(rbFtNone, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); connect(rbFtNone, SIGNAL(clicked(bool)), rbVlanNone, SLOT(click())); +#endif + + // Force all protocols = None if L1 = None + connect(rbL1None, SIGNAL(clicked(bool)), rbVlanNone, SLOT(click())); + connect(rbL1None, SIGNAL(clicked(bool)), rbFtNone, SLOT(click())); + connect(rbL1None, SIGNAL(clicked(bool)), rbPayloadNone, SLOT(click())); + connect(rbFtNone, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); // Enable/Disable L3 Protocol Choices for FT None @@ -173,36 +183,43 @@ void StreamConfigDialog::setupUiExtra() QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); // ---- Setup default stuff that cannot be done in designer ---- +#if 0 gbVlan->setDisabled(true); +#endif - bgFrameType = new QButtonGroup(); + bgL1Proto = new QButtonGroup(); + bgL1Proto->addButton(rbL1None, ButtonIdNone); + bgL1Proto->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgL1Proto->addButton(rbL1Other, ButtonIdOther); + + bgL2Proto = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbFrameType->findChildren()) - bgFrameType->addButton(btn); + bgL2Proto->addButton(btn); #else - bgFrameType->addButton(rbFtNone, 0); - bgFrameType->addButton(rbFtEthernet2, 121); - bgFrameType->addButton(rbFt802Dot3Raw, 122); - bgFrameType->addButton(rbFt802Dot3Llc, 123); - bgFrameType->addButton(rbFtLlcSnap, 124); - bgFrameType->addButton(rbFtOther, -1); + bgL2Proto->addButton(rbFtNone, ButtonIdNone); + bgL2Proto->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgL2Proto->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgL2Proto->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgL2Proto->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgL2Proto->addButton(rbFtOther, ButtonIdOther); #endif bgVlan = new QButtonGroup(); - bgVlan->addButton(rbVlanNone, 0); - bgVlan->addButton(rbVlanSingle, 126); - bgVlan->addButton(rbVlanDouble, 127); + bgVlan->addButton(rbVlanNone, ButtonIdNone); + bgVlan->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgVlan->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); bgL3Proto = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL3Proto->findChildren()) bgL3Proto->addButton(btn); #else - bgL3Proto->addButton(rbL3None, 0); - bgL3Proto->addButton(rbL3Ipv4, 130); - bgL3Proto->addButton(rbL3Ipv6, -1); - bgL3Proto->addButton(rbL3Arp, -1); - bgL3Proto->addButton(rbL3Other, -1); + bgL3Proto->addButton(rbL3None, ButtonIdNone); + bgL3Proto->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgL3Proto->addButton(rbL3Ipv6, 0xFFFF); + bgL3Proto->addButton(rbL3Arp, 0xFFFF); + bgL3Proto->addButton(rbL3Other, ButtonIdOther); #endif bgL4Proto = new QButtonGroup(); @@ -211,11 +228,11 @@ void StreamConfigDialog::setupUiExtra() bgL4Proto->addButton(btn); #else bgL4Proto->addButton(rbL4None, 0); - bgL4Proto->addButton(rbL4Tcp, 140); - bgL4Proto->addButton(rbL4Udp, 141); - bgL4Proto->addButton(rbL4Icmp, -1); - bgL4Proto->addButton(rbL4Igmp, -1); - bgL4Proto->addButton(rbL4Other, -1); + bgL4Proto->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgL4Proto->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgL4Proto->addButton(rbL4Icmp, 0xFFFF); + bgL4Proto->addButton(rbL4Igmp, 0xFFFF); + bgL4Proto->addButton(rbL4Other, ButtonIdOther); #endif bgPayloadProto = new QButtonGroup(); @@ -223,8 +240,9 @@ void StreamConfigDialog::setupUiExtra() foreach(QRadioButton *btn, gbPayloadProto->findChildren()) bgPayloadProto->addButton(btn); #else - bgPayloadProto->addButton(rbPayloadPattern, 52); - bgPayloadProto->addButton(rbPayloadOther, -1); + bgPayloadProto->addButton(rbPayloadNone, ButtonIdNone); + bgPayloadProto->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgPayloadProto->addButton(rbPayloadOther, ButtonIdOther); #endif /* ** Setup Validators @@ -252,7 +270,8 @@ StreamConfigDialog::~StreamConfigDialog() delete mpPacketModelTester; delete mpPacketModel; - delete bgFrameType; + delete bgL1Proto; + delete bgL2Proto; delete bgVlan; delete bgL3Proto; delete bgL4Proto; @@ -573,91 +592,93 @@ void StreamConfigDialog::on_lePattern_editingFinished() } #endif +/*! +Skip protocols upto and including the layer specified. + 0 - L1 + 1 - VLAN + 2 - L2 + 3 - L3 + 4 - L4 +TODO: Convert the above values to enum?? +*/ bool StreamConfigDialog::skipProtocols(int layer) { -#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) + int id; + QAbstractButton *btn; _iter->toFront(); - // Check and skip 'mac' - if (CHK(51)) - _iter->next(); - else - goto _unexpected_proto; + // Skip L1 + if (_iter->hasNext()) + { + id = _iter->next()->protocolNumber(); + btn = bgL1Proto->button(id); + if (btn == NULL) + _iter->previous(); + } if (layer == 0) goto _done; - // Skip VLANs - while (CHK(126)) - _iter->next(); + // Skip VLAN + if(_iter->hasNext()) + { + id = _iter->next()->protocolNumber(); + btn = bgVlan->button(id); + if (btn == NULL) + _iter->previous(); + } if (layer == 1) goto _done; - // Skip L2 (FrameType) - if (CHK(121)) // Eth2 - _iter->next(); - else if (CHK(122)) // 802.3 RAW + // Skip L2 + if(_iter->hasNext()) { - _iter->next(); - if (CHK(123)) // 802.3 LLC - { - _iter->next(); - if (CHK(124)) // SNAP - _iter->next(); - } + id = _iter->next()->protocolNumber(); + btn = bgL2Proto->button(id); + if (btn == NULL) + _iter->previous(); } - else - goto _unexpected_proto; if (layer == 2) goto _done; // Skip L3 - if (CHK(130)) // IP4 - _iter->next(); - else if (CHK(131)) // ARP - _iter->next(); - else - goto _unexpected_proto; + if (_iter->hasNext()) + { + id = _iter->next()->protocolNumber(); + btn = bgL3Proto->button(id); + if (btn == NULL) + _iter->previous(); + } if (layer == 3) goto _done; // Skip L4 - if (CHK(140)) // TCP - _iter->next(); - else if (CHK(141)) // UDP - _iter->next(); - else if (CHK(142)) // ICMP - _iter->next(); - else if (CHK(143)) // IGMP - _iter->next(); - else - goto _unexpected_proto; + if(_iter->hasNext()) + { + id = _iter->next()->protocolNumber(); + btn = bgL4Proto->button(id); + if (btn == NULL) + _iter->previous(); + } if (layer == 4) goto _done; - goto _unexpected_proto; + return false; _done: return true; - -_unexpected_proto: - qWarning("%s: unexpected protocol", __FUNCTION__); - return false; - -#undef CHK } -void StreamConfigDialog::updateFrameTypeProtocol(int newId) +void StreamConfigDialog::updateL1Protocol(int newId) { - static int oldId = 0; - AbstractProtocol *p; + static int oldId; - qDebug("%s:old id = %d new id = %d, upd? = %d", __FUNCTION__, oldId, newId, + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, isUpdateInProgress); if (oldId == newId) @@ -665,111 +686,41 @@ void StreamConfigDialog::updateFrameTypeProtocol(int newId) if (!isUpdateInProgress) { - Q_ASSERT(newId != -1); + AbstractProtocol *p; - if (!skipProtocols(1)) - goto _error; + _iter->toFront(); + + Q_ASSERT(newId != ButtonIdOther); - // Delete old Id switch (oldId) { - case 0: - break; - case -1: - // Blindly remove the current protocol - p =_iter->next(); - _iter->remove(); - delete p; + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); break; - case 121: // ethernet - case 122: // dot3 - p =_iter->next(); - if (p->protocolNumber() != (quint32) oldId) - goto _error; - - _iter->remove(); - delete p; - break; - case 123: // dot3 llc - p =_iter->next(); - if (p->protocolNumber() != 122) - goto _error; - _iter->remove(); - delete p; - - p =_iter->next(); - if (p->protocolNumber() != 123) - goto _error; - _iter->remove(); - delete p; - break; - case 124: // dot3 llc snap - p =_iter->next(); - if (p->protocolNumber() != 122) - goto _error; - _iter->remove(); - delete p; - - p =_iter->next(); - if (p->protocolNumber() != 123) - goto _error; - _iter->remove(); - delete p; - - p =_iter->next(); - if (p->protocolNumber() != 124) - goto _error; - _iter->remove(); - delete p; - break; + case ButtonIdOther: default: - goto _error; - } + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); - // Insert new Id - switch (newId) - { - case 0: + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; break; - case 121: // ethernet - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - case 122: // dot3 - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - case 123: // dot3 llc - _iter->insert(OstProtocolManager.createProtocol( - 122, mpStream)); - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - case 124: // dot3 llc snap - _iter->insert(OstProtocolManager.createProtocol( - 122, mpStream)); - _iter->insert(OstProtocolManager.createProtocol( - 123, mpStream)); - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - default: - goto _error; } } oldId = newId; return; - -_error: - qFatal("%s: unexpected incident", __FUNCTION__); } void StreamConfigDialog::updateVlanProtocol(int newId) { - static int oldId = 0; - AbstractProtocol *p; + static int oldId; qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, isUpdateInProgress); @@ -779,101 +730,90 @@ void StreamConfigDialog::updateVlanProtocol(int newId) if (!isUpdateInProgress) { - Q_ASSERT(newId != -1); + int ret; + AbstractProtocol *p; - if (!skipProtocols(0)) - goto _error; + ret = skipProtocols(0); + + Q_ASSERT(ret == true); + Q_ASSERT(oldId != ButtonIdOther); + Q_ASSERT(newId != ButtonIdOther); - // Delete oldId proto switch (oldId) { - case 0: - switch (newId) - { - case 127: - _iter->insert(OstProtocolManager.createProtocol( - 126, mpStream)); - // No 'break'; fallthrough - by design! - case 126: - _iter->insert(OstProtocolManager.createProtocol( - 126, mpStream)); - break; - default: - goto _error; - } + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); break; - case 126: - if (!_iter->hasNext()) - goto _error; - p =_iter->next(); - if (p->protocolNumber() != 126) - goto _error; - switch (newId) - { - case 0: - _iter->remove(); - delete p; - break; - - case 127: - _iter->insert(OstProtocolManager.createProtocol( - 126, mpStream)); - break; - default: - goto _error; - } - break; - - case 127: - if (!_iter->hasNext()) - goto _error; - p =_iter->next(); - if (p->protocolNumber() != 126) - goto _error; - p =_iter->next(); - if (p->protocolNumber() != 126) - goto _error; - switch (newId) - { - case 0: - _iter->previous(); - _iter->previous(); - p =_iter->next(); - _iter->remove(); - delete p; - p =_iter->next(); - _iter->remove(); - delete p; - break; - - case 126: - _iter->previous(); - _iter->previous(); - p =_iter->next(); - _iter->remove(); - delete p; - break; - default: - goto _error; - } - break; + case ButtonIdOther: default: - goto _error; + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + break; } } - + oldId = newId; return; +} -_error: - qFatal("%s: unexpected incident", __FUNCTION__); +void StreamConfigDialog::updateFrameTypeProtocol(int newId) +{ + static int oldId; + + qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, + isUpdateInProgress); + + if (oldId == newId) + return; // Nothing to be done + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(1); + + Q_ASSERT(ret == true); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + break; + } + } + + oldId = newId; + return; } void StreamConfigDialog::updateL3Protocol(int newId) { - static int oldId = 0; - AbstractProtocol *p; + static int oldId; qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, isUpdateInProgress); @@ -883,28 +823,26 @@ void StreamConfigDialog::updateL3Protocol(int newId) if (!isUpdateInProgress) { - Q_ASSERT(newId != -1); + int ret; + AbstractProtocol *p; - if (!skipProtocols(2)) - goto _error; + ret = skipProtocols(2); + + Q_ASSERT(ret == true); + Q_ASSERT(newId != ButtonIdOther); switch (oldId) { - case 0: + case ButtonIdNone: _iter->insert(OstProtocolManager.createProtocol( newId, mpStream)); break; - case -1: + case ButtonIdOther: default: - if (!_iter->hasNext()) - goto _error; - + Q_ASSERT(_iter->hasNext()); p =_iter->next(); - if ((oldId != -1) && (p->protocolNumber() == (quint32) newId)) - goto _error; - if (newId) _iter->setValue(OstProtocolManager.createProtocol( newId, mpStream)); @@ -917,15 +855,11 @@ void StreamConfigDialog::updateL3Protocol(int newId) oldId = newId; return; - -_error: - qFatal("%s: unexpected incident", __FUNCTION__); } void StreamConfigDialog::updateL4Protocol(int newId) { - static int oldId = 0; - AbstractProtocol *p; + static int oldId; qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, isUpdateInProgress); @@ -935,28 +869,26 @@ void StreamConfigDialog::updateL4Protocol(int newId) if (!isUpdateInProgress) { - Q_ASSERT(newId != -1); + int ret; + AbstractProtocol *p; - if (!skipProtocols(3)) - goto _error; + ret = skipProtocols(3); + + Q_ASSERT(ret == true); + Q_ASSERT(newId != ButtonIdOther); switch (oldId) { - case 0: + case ButtonIdNone: _iter->insert(OstProtocolManager.createProtocol( newId, mpStream)); break; - case -1: + case ButtonIdOther: default: - if (!_iter->hasNext()) - goto _error; - + Q_ASSERT(_iter->hasNext()); p =_iter->next(); - if ((oldId != -1) && (p->protocolNumber() == (quint32) newId)) - goto _error; - if (newId) _iter->setValue(OstProtocolManager.createProtocol( newId, mpStream)); @@ -969,15 +901,11 @@ void StreamConfigDialog::updateL4Protocol(int newId) oldId = newId; return; - -_error: - qFatal("%s: unexpected incident", __FUNCTION__); } void StreamConfigDialog::updatePayloadProtocol(int newId) { - static int oldId = 52; - AbstractProtocol *p; + static int oldId; qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, isUpdateInProgress); @@ -987,219 +915,189 @@ void StreamConfigDialog::updatePayloadProtocol(int newId) if (!isUpdateInProgress) { - Q_ASSERT(newId != 0); - Q_ASSERT(newId != -1); + int ret; + AbstractProtocol *p; - if (!skipProtocols(4)) - goto _error; + ret = skipProtocols(4); + + Q_ASSERT(ret == true); + Q_ASSERT(newId != ButtonIdOther); - if (!_iter->hasNext()) - goto _error; - - p =_iter->next(); - - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - delete p; - while (_iter->hasNext()) + switch (oldId) { + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; - p = _iter->next(); - _iter->remove(); - delete p; + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + while (_iter->hasNext()) + { + + p = _iter->next(); + _iter->remove(); + delete p; + } + break; } } oldId = newId; return; - -_error: - qFatal("%s: unexpected incident", __FUNCTION__); } void StreamConfigDialog::updateSelectProtocolsSimpleWidget() { -#define CHK(p) (_iter->hasNext() && _iter->peekNext()->protocolNumber() == p) + quint32 id; + QAbstractButton *btn; qDebug("%s", __FUNCTION__); isUpdateInProgress = true; + // Reset to default state + rbL1None->setChecked(true); + rbVlanNone->setChecked(true); + rbFtNone->setChecked(true); + rbL3None->setChecked(true); + rbL4None->setChecked(true); + rbPayloadNone->setChecked(true); + _iter->toFront(); - // - // We expect first protocol to be 'mac' usually - // - if (CHK(51)) // Mac - { - _iter->next(); - } + // L1 (optional if followed by Payload) + if (!_iter->hasNext()) // No protocols at all? + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgL1Proto->button(id); + + if (btn && btn->isEnabled()) + btn->click(); else { - rbFtOther->setChecked(true); - goto _done; - } - - - // - // Check for "VLAN" - // - if (CHK(126)) // VLAN - { - _iter->next(); - if (CHK(126)) // VLAN - { - rbVlanDouble->setChecked(true); - updateVlanProtocol(127); - _iter->next(); - } + btn = bgPayloadProto->button(id); + if (btn && btn->isEnabled()) + goto _payload; else - { - rbVlanSingle->setChecked(true); - updateVlanProtocol(126); - } - } - else - { - rbVlanNone->setChecked(true); - updateVlanProtocol(0); + goto _otherL1; } - - // - // Identify and set "FrameType" - // - if (CHK(52)) // Payload - { - rbFtNone->click(); - goto _payload; - } - else if (CHK(121)) // Eth2 - { - rbFtEthernet2->click(); - _iter->next(); - } - else if (CHK(122)) // 802.3 RAW - { - _iter->next(); - if (CHK(123)) // 802.3 LLC - { - _iter->next(); - if (CHK(124)) // SNAP - { - rbFtLlcSnap->click(); - _iter->next(); - } - else - { - rbFt802Dot3Llc->click(); - } - } - else - { - rbFt802Dot3Raw->click(); - } - } - else - { - rbFtOther->setChecked(true); - goto _done; - } - - - // - // --- L3 --- - // - if (CHK(130)) // IP4 - { - rbL3Ipv4->click(); - _iter->next(); - } - else if (CHK(131)) // ARP - { - rbL3Arp->click(); - _iter->next(); - } - else if (CHK(52)) // Payload - { - rbL3None->click(); - goto _payload; - } - else - { - rbL3Other->setChecked(true); - goto _done; - } - - // - // --- L4 --- - // + // VLAN (optional) if (!_iter->hasNext()) - { - rbL4Other->setChecked(true); goto _done; - } - else if (CHK(140)) // TCP - { - rbL4Tcp->click(); - _iter->next(); - } - else if (CHK(141)) // UDP - { - rbL4Udp->click(); - _iter->next(); - } - else if (CHK(142)) // ICMP - { - rbL4Icmp->click(); - _iter->next(); - } - else if (CHK(143)) // IGMP - { - rbL4Igmp->click(); - _iter->next(); - } - else if (CHK(52)) // Payload - { - rbL4None->click(); - goto _payload; - } + + id = _iter->next()->protocolNumber(); + btn = bgVlan->button(id); + + if (btn && btn->isEnabled()) + btn->click(); + else + _iter->previous(); + + // L2 (optional if followed by Payload) + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgL2Proto->button(id); + + if (btn && btn->isEnabled()) + btn->click(); else { - rbL4Other->setChecked(true); - goto _done; + btn = bgPayloadProto->button(id); + if (btn && btn->isEnabled()) + goto _payload; + else + goto _otherL2; } + // L3 (optional if followed by Payload) + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgL3Proto->button(id); + + if (btn && btn->isEnabled()) + btn->click(); + else + { + btn = bgPayloadProto->button(id); + if (btn && btn->isEnabled()) + goto _payload; + else + goto _otherL3; + } + + // L4 (optional if followed by Payload) + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgL4Proto->button(id); + + if (btn && btn->isEnabled()) + btn->click(); + else + { + btn = bgPayloadProto->button(id); + if (btn && btn->isEnabled()) + goto _payload; + else + goto _otherL4; + } + + // Payload Data + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgPayloadProto->button(id); + _payload: - // - // Payload - // - if (CHK(52)) // Payload - { - rbPayloadPattern->click(); - _iter->next(); - } + if (btn && btn->isEnabled()) + btn->click(); + else + goto _otherPayload; - // If there is any protocol beyond "data pattern" ... + // If more protocol(s) beyond payload ... if (_iter->hasNext()) - rbPayloadOther->setChecked(true); + goto _otherPayload; + + goto _done; + +_otherL1: + bgL1Proto->button(ButtonIdOther)->setChecked(true); + updateL1Protocol(ButtonIdOther); +_otherL2: + bgL2Proto->button(ButtonIdOther)->setChecked(true); + updateFrameTypeProtocol(ButtonIdOther); +_otherL3: + bgL3Proto->button(ButtonIdOther)->setChecked(true); + updateL3Protocol(ButtonIdOther); +_otherL4: + bgL4Proto->button(ButtonIdOther)->setChecked(true); + updateL4Protocol(ButtonIdOther); +_otherPayload: + bgPayloadProto->button(ButtonIdOther)->setChecked(true); + updatePayloadProtocol(ButtonIdOther); _done: - // If any "other" protocols are checked, QButtonGroup signals - // are not triggered since the "other" radioButton's are disabled - // - so update manually - if (rbFtOther->isChecked()) - updateFrameTypeProtocol(-1); - if (rbL3Other->isChecked()) - updateL3Protocol(-1); - if (rbL4Other->isChecked()) - updateL4Protocol(-1); - if (rbPayloadOther->isChecked()) - updatePayloadProtocol(-1); isUpdateInProgress = false; return; -#undef CHK } void StreamConfigDialog::LoadCurrentStream() From 0094f618d34683dbb3844da646115c1584b6f52b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 14 Oct 2009 15:16:56 +0000 Subject: [PATCH 026/294] Protocol Framework related -------------------------- - AbstractProtocol Constructor and Factory function now take an optional (default NULL) "parent" abstract protocol in addition to the stream; this "parent" protocol is non-NULL for protocols which are aggregated in a ComboProtocol - All subclasses of AbstractProtocol modified as per the above interface change - ProtocolManager also modifed as per the above interface change - new data members in AbstractProtocol - prev, next; the AbstractProtocol implementation now uses these members to traverse protocols on the list instead of ProtocolListIterator; this change required for ComboProtocol - ProtocolListIterator updates these new members - prev/next on insert/remove/replace - ComboProtocol and ProtocolListIterator classes made friends of AbstractProtocol - ComboProtocol implemented as a template class (completed) - Dot2LLc implemented as a combo of Dot3Raw and LLC - Dot2Snap implemented as a combo of Dot2Llc and SNAP - VlanStack implemented as a combo of VLAN + VLAN - ProtocolManager now uses the ProtocolId enums rather than hardcoded values Stream Config Dialog -------------------- - "None" radio button added to all protocol levels - Protocol Level 1 added with 'mac' as the only valid protocol in the "simple" mode widget - With Dot2Llc, Dot2Snap and VlanStack implemented as "combo" protocols, the protocol choice radiobuttons in the "simple" mode are now 1:1 with a protocol; this has the following implications/advantages: - Updates of the "simple" mode widget from/to stream's protocolList is simpler; this code has now been rewritten to take advantage of 1:1 - This paves the way for exporting tunneled protocols 4over4, 4over6, 6over4 etc. in the "simple" mode - This should also (hopefully) require less changes when adding a new protocol; more work needs to be done to reach this goal Fixes ----- - Dot3Protocol now derives "length" correctly for VLAN tagged packets - StreamBase now uses the ProtocolListIterator to append the default protocols in a stream instead of directly manipulating ProtocolList; also in protoDataCopyFrom() Others (Client/Server) ---------------------- - Port Packet Capture implemented; "view capture" is pending (hack put in place now for testing) --- client/packetmodel.cpp | 5 +- client/portgroup.cpp | 108 +++++ client/portgroup.h | 7 + client/portstatswindow.cpp | 36 ++ client/streamconfigdialog.cpp | 711 +++++++++----------------------- client/streamconfigdialog.h | 34 +- client/streamconfigdialog.ui | 224 ++++++---- common/abstractprotocol.cpp | 101 ++--- common/abstractprotocol.h | 15 +- common/comboprotocol.h | 87 ++-- common/dot2llc.h | 11 + common/dot2llc.proto | 14 + common/dot2snap.h | 11 + common/dot2snap.proto | 12 + common/dot3.cpp | 20 +- common/dot3.h | 5 +- common/eth2.cpp | 9 +- common/eth2.h | 5 +- common/ip4.cpp | 11 +- common/ip4.h | 5 +- common/llc.cpp | 9 +- common/llc.h | 5 +- common/mac.cpp | 9 +- common/mac.h | 5 +- common/ostproto.pro | 6 + common/payload.cpp | 9 +- common/payload.h | 5 +- common/protocol.proto | 4 + common/protocollistiterator.cpp | 31 +- common/protocollistiterator.h | 4 +- common/protocolmanager.cpp | 51 ++- common/protocolmanager.h | 6 +- common/sample.cpp | 9 +- common/sample.h | 5 +- common/snap.cpp | 9 +- common/snap.h | 5 +- common/streambase.cpp | 18 +- common/tcp.cpp | 9 +- common/tcp.h | 5 +- common/udp.cpp | 9 +- common/udp.h | 5 +- common/vlan.cpp | 9 +- common/vlan.h | 5 +- common/vlanstack.h | 10 + common/vlanstack.proto | 12 + server/myservice.cpp | 109 ++++- server/myservice.h | 21 + 47 files changed, 1029 insertions(+), 786 deletions(-) create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 7d9df3d..908de63 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -46,7 +46,7 @@ int PacketModel::rowCount(const QModelIndex &parent) const qWarning("%s: Unhandled ItemType", __FUNCTION__); } - Q_ASSERT(1 == 1); // Unreachable code + Q_ASSERT(1 == 0); // Unreachable code qWarning("%s: Catch all - need to investigate", __FUNCTION__); return 0; // catch all } @@ -86,13 +86,14 @@ QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) cons goto _exit; case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code goto _exit; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } - Q_ASSERT(1 == 1); // Unreachable code + Q_ASSERT(1 == 0); // Unreachable code _exit: return index; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index f9f84d2..4614528 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -479,6 +479,93 @@ _exit: return; } +void PortGroup::startCapture(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->startCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, ack)); +_exit: + return; +} + +void PortGroup::stopCapture(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->stopCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, ack)); +_exit: + return; +} + +void PortGroup::viewCapture(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::CaptureBufferList *bufList; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + bufList = new OstProto::CaptureBufferList; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->getCaptureBuffer(rpcController, &portIdList, bufList, + NewCallback(this, &PortGroup::processViewCaptureAck, bufList)); +_exit: + return; +} + void PortGroup::processStartTxAck(OstProto::Ack *ack) { qDebug("In %s", __FUNCTION__); @@ -493,6 +580,27 @@ void PortGroup::processStopTxAck(OstProto::Ack *ack) delete ack; } +void PortGroup::processStartCaptureAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processStopCaptureAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processViewCaptureAck(OstProto::CaptureBufferList *bufList) +{ + qDebug("In %s", __FUNCTION__); + + delete bufList; +} + void PortGroup::getPortStats() { OstProto::PortStatsList *portStatsList; diff --git a/client/portgroup.h b/client/portgroup.h index 2ffcbaf..c297674 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -75,6 +75,13 @@ public: void stopTx(QList *portList = NULL); void processStopTxAck(OstProto::Ack *ack); + void startCapture(QList *portList = NULL); + void processStartCaptureAck(OstProto::Ack *ack); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(OstProto::Ack *ack); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(OstProto::CaptureBufferList *bufList); + void getPortStats(); void processPortStatsList(OstProto::PortStatsList *portStatsList); void clearPortStats(QList *portList = NULL); diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index d1340a6..26e2b10 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -61,16 +61,52 @@ void PortStatsWindow::on_tbStopTransmit_clicked() void PortStatsWindow::on_tbStartCapture_clicked() { // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbStopCapture_clicked() { // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbViewCapture_clicked() { // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbClear_clicked() diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 9bdae53..30649b3 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -27,18 +27,13 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, setupUi(this); setupUiExtra(); - connect(bgL1Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateL1Protocol(int))); - connect(bgL2Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateFrameTypeProtocol(int))); - connect(bgVlan, SIGNAL(buttonClicked(int)), - this, SLOT(updateVlanProtocol(int))); - connect(bgL3Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateL3Protocol(int))); - connect(bgL4Proto, SIGNAL(buttonClicked(int)), - this, SLOT(updateL4Protocol(int))); - connect(bgPayloadProto, SIGNAL(buttonClicked(int)), - this, SLOT(updatePayloadProtocol(int))); + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } //! \todo causes a crash! #if 0 @@ -48,35 +43,16 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Time to play match the signals and slots! - // Enable VLAN Choices only if FT = Eth2 or SNAP -#if 0 - connect(rbFtNone, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); - connect(rbFtOther, SIGNAL(toggled(bool)), gbVlan, SLOT(setDisabled(bool))); - connect(rbFtNone, SIGNAL(clicked(bool)), rbVlanNone, SLOT(click())); -#endif - - // Force all protocols = None if L1 = None - connect(rbL1None, SIGNAL(clicked(bool)), rbVlanNone, SLOT(click())); - connect(rbL1None, SIGNAL(clicked(bool)), rbFtNone, SLOT(click())); - connect(rbL1None, SIGNAL(clicked(bool)), rbPayloadNone, SLOT(click())); - - connect(rbFtNone, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); - - // Enable/Disable L3 Protocol Choices for FT None - connect(rbFtNone, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); + // If L1/FT = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); // Enable/Disable L3 Protocol Choices for FT Ethernet2 - connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); // Force L3 = None if FT = 802.3 Raw connect(rbFt802Dot3Raw, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); - - // Enable/Disable L3 Protocol Choices for FT 802Dot3Raw - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); @@ -84,12 +60,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFt802Dot3Llc, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); // Enable/Disable L3 Protocol Choices for FT 802Dot3Llc - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); // Enable/Disable L3 Protocol Choices for FT 802.3 LLC SNAP - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3None, SLOT(setEnabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); @@ -97,25 +71,16 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); - // Enable/Disable L4 Protocol Choices for L3 Protocol None - connect(rbL3None, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); - - // Force L4 Protocol = None if L3 Protocol is set to None - connect(rbL3None, SIGNAL(clicked(bool)), rbL4None, SLOT(click())); + // If L3 = None, force subsequent protocol level also to None + connect(rbL3None, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); // Enable/Disable L4 Protocol Choices for L3 Protocol IPv4 - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setEnabled(bool))); connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setEnabled(bool))); // Enable/Disable L4 Protocol Choices for L3 Protocol ARP - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4None, SLOT(setEnabled(bool))); connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); @@ -183,66 +148,62 @@ void StreamConfigDialog::setupUiExtra() QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); // ---- Setup default stuff that cannot be done in designer ---- -#if 0 - gbVlan->setDisabled(true); -#endif + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); - bgL1Proto = new QButtonGroup(); - bgL1Proto->addButton(rbL1None, ButtonIdNone); - bgL1Proto->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); - bgL1Proto->addButton(rbL1Other, ButtonIdOther); - - bgL2Proto = new QButtonGroup(); + bgProto[ProtoL2] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbFrameType->findChildren()) bgL2Proto->addButton(btn); #else - bgL2Proto->addButton(rbFtNone, ButtonIdNone); - bgL2Proto->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); - bgL2Proto->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); - bgL2Proto->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); - bgL2Proto->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); - bgL2Proto->addButton(rbFtOther, ButtonIdOther); + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); #endif - bgVlan = new QButtonGroup(); - bgVlan->addButton(rbVlanNone, ButtonIdNone); - bgVlan->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); - bgVlan->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); - bgL3Proto = new QButtonGroup(); + bgProto[ProtoL3] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL3Proto->findChildren()) - bgL3Proto->addButton(btn); + bgProto[ProtoL3]->addButton(btn); #else - bgL3Proto->addButton(rbL3None, ButtonIdNone); - bgL3Proto->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); - bgL3Proto->addButton(rbL3Ipv6, 0xFFFF); - bgL3Proto->addButton(rbL3Arp, 0xFFFF); - bgL3Proto->addButton(rbL3Other, ButtonIdOther); + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif - bgL4Proto = new QButtonGroup(); + bgProto[ProtoL4] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL4Proto->findChildren()) - bgL4Proto->addButton(btn); + bgProto[ProtoL4]->addButton(btn); #else - bgL4Proto->addButton(rbL4None, 0); - bgL4Proto->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); - bgL4Proto->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); - bgL4Proto->addButton(rbL4Icmp, 0xFFFF); - bgL4Proto->addButton(rbL4Igmp, 0xFFFF); - bgL4Proto->addButton(rbL4Other, ButtonIdOther); + bgProto[ProtoL4]->addButton(rbL4None, 0); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); #endif - bgPayloadProto = new QButtonGroup(); + bgProto[ProtoPayload] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbPayloadProto->findChildren()) - bgPayloadProto->addButton(btn); + bgProto[ProtoPayload]->addButton(btn); #else - bgPayloadProto->addButton(rbPayloadNone, ButtonIdNone); - bgPayloadProto->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); - bgPayloadProto->addButton(rbPayloadOther, ButtonIdOther); + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); #endif /* ** Setup Validators @@ -270,12 +231,8 @@ StreamConfigDialog::~StreamConfigDialog() delete mpPacketModelTester; delete mpPacketModel; - delete bgL1Proto; - delete bgL2Proto; - delete bgVlan; - delete bgL3Proto; - delete bgL4Proto; - delete bgPayloadProto; + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; delete _iter; delete mpStream; @@ -594,149 +551,122 @@ void StreamConfigDialog::on_lePattern_editingFinished() /*! Skip protocols upto and including the layer specified. - 0 - L1 - 1 - VLAN - 2 - L2 - 3 - L3 - 4 - L4 -TODO: Convert the above values to enum?? */ bool StreamConfigDialog::skipProtocols(int layer) { - int id; - QAbstractButton *btn; - _iter->toFront(); - // Skip L1 - if (_iter->hasNext()) + for (int i = ProtoMin; i <= layer; i++) { - id = _iter->next()->protocolNumber(); - btn = bgL1Proto->button(id); - if (btn == NULL) - _iter->previous(); + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } } - if (layer == 0) - goto _done; - - // Skip VLAN - if(_iter->hasNext()) - { - id = _iter->next()->protocolNumber(); - btn = bgVlan->button(id); - if (btn == NULL) - _iter->previous(); - } - - if (layer == 1) - goto _done; - - // Skip L2 - if(_iter->hasNext()) - { - id = _iter->next()->protocolNumber(); - btn = bgL2Proto->button(id); - if (btn == NULL) - _iter->previous(); - } - - if (layer == 2) - goto _done; - - // Skip L3 - if (_iter->hasNext()) - { - id = _iter->next()->protocolNumber(); - btn = bgL3Proto->button(id); - if (btn == NULL) - _iter->previous(); - } - - if (layer == 3) - goto _done; - - // Skip L4 - if(_iter->hasNext()) - { - id = _iter->next()->protocolNumber(); - btn = bgL4Proto->button(id); - if (btn == NULL) - _iter->previous(); - } - - if (layer == 4) - goto _done; - - return false; - -_done: return true; } -void StreamConfigDialog::updateL1Protocol(int newId) +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) { - static int oldId; - - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); - - if (oldId == newId) - return; // Nothing to be done - - if (!isUpdateInProgress) + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) { - AbstractProtocol *p; + int id = protocolGroup->id(btn); - _iter->toFront(); - - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - break; - } + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); } - - oldId = newId; - return; } -void StreamConfigDialog::updateVlanProtocol(int newId) +void StreamConfigDialog::forceProtocolNone(bool checked) { - static int oldId; + QObject *btn; - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); + btn = sender(); + Q_ASSERT(btn != NULL); - if (oldId == newId) - return; // Nothing to be done + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; if (!isUpdateInProgress) { int ret; AbstractProtocol *p; - ret = skipProtocols(0); - + ret = skipProtocols(level-1); Q_ASSERT(ret == true); - Q_ASSERT(oldId != ButtonIdOther); + + Q_ASSERT(oldId != newId); Q_ASSERT(newId != ButtonIdOther); switch (oldId) @@ -757,347 +687,96 @@ void StreamConfigDialog::updateVlanProtocol(int newId) else _iter->remove(); delete p; - break; - } - } - - oldId = newId; - return; -} - -void StreamConfigDialog::updateFrameTypeProtocol(int newId) -{ - static int oldId; - - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); - - if (oldId == newId) - return; // Nothing to be done - - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; - - ret = skipProtocols(1); - - Q_ASSERT(ret == true); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - break; - } - } - - oldId = newId; - return; -} - -void StreamConfigDialog::updateL3Protocol(int newId) -{ - static int oldId; - - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); - - if (oldId == newId) - return; // Nothing to be done - - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; - - ret = skipProtocols(2); - - Q_ASSERT(ret == true); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - break; - } - } - - oldId = newId; - return; -} - -void StreamConfigDialog::updateL4Protocol(int newId) -{ - static int oldId; - - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); - - if (oldId == newId) - return; // Nothing to be done - - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; - - ret = skipProtocols(3); - - Q_ASSERT(ret == true); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - break; - } - } - - oldId = newId; - return; -} - -void StreamConfigDialog::updatePayloadProtocol(int newId) -{ - static int oldId; - - qDebug("%s:old id = %d new id = %d upd? = %d", __FUNCTION__, oldId, newId, - isUpdateInProgress); - - if (oldId == newId) - return; // Nothing to be done - - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; - - ret = skipProtocols(4); - - Q_ASSERT(ret == true); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - while (_iter->hasNext()) + if (level == ProtoPayload) { - - p = _iter->next(); - _iter->remove(); - delete p; + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } } break; } } - oldId = newId; + btnGrp->setProperty("ProtocolId", newId); return; } void StreamConfigDialog::updateSelectProtocolsSimpleWidget() { - quint32 id; + int i; + quint32 id; QAbstractButton *btn; qDebug("%s", __FUNCTION__); isUpdateInProgress = true; - // Reset to default state - rbL1None->setChecked(true); - rbVlanNone->setChecked(true); - rbFtNone->setChecked(true); - rbL3None->setChecked(true); - rbL4None->setChecked(true); - rbPayloadNone->setChecked(true); + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + // ... now iterate and update _iter->toFront(); - // L1 (optional if followed by Payload) - if (!_iter->hasNext()) // No protocols at all? - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgL1Proto->button(id); - - if (btn && btn->isEnabled()) - btn->click(); - else + for (i = ProtoMin; i < ProtoMax; i++) { - btn = bgPayloadProto->button(id); + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn && btn->isEnabled()) - goto _payload; + btn->click(); else - goto _otherL1; + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } } - // VLAN (optional) - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgVlan->button(id); - - if (btn && btn->isEnabled()) - btn->click(); - else - _iter->previous(); - - // L2 (optional if followed by Payload) - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgL2Proto->button(id); - - if (btn && btn->isEnabled()) - btn->click(); - else - { - btn = bgPayloadProto->button(id); - if (btn && btn->isEnabled()) - goto _payload; - else - goto _otherL2; - } - - // L3 (optional if followed by Payload) - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgL3Proto->button(id); - - if (btn && btn->isEnabled()) - btn->click(); - else - { - btn = bgPayloadProto->button(id); - if (btn && btn->isEnabled()) - goto _payload; - else - goto _otherL3; - } - - // L4 (optional if followed by Payload) - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgL4Proto->button(id); - - if (btn && btn->isEnabled()) - btn->click(); - else - { - btn = bgPayloadProto->button(id); - if (btn && btn->isEnabled()) - goto _payload; - else - goto _otherL4; - } - - // Payload Data - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgPayloadProto->button(id); - -_payload: - if (btn && btn->isEnabled()) - btn->click(); - else - goto _otherPayload; - // If more protocol(s) beyond payload ... if (_iter->hasNext()) - goto _otherPayload; + { + i = ProtoPayload; + goto _other; + } goto _done; -_otherL1: - bgL1Proto->button(ButtonIdOther)->setChecked(true); - updateL1Protocol(ButtonIdOther); -_otherL2: - bgL2Proto->button(ButtonIdOther)->setChecked(true); - updateFrameTypeProtocol(ButtonIdOther); -_otherL3: - bgL3Proto->button(ButtonIdOther)->setChecked(true); - updateL3Protocol(ButtonIdOther); -_otherL4: - bgL4Proto->button(ButtonIdOther)->setChecked(true); - updateL4Protocol(ButtonIdOther); -_otherPayload: - bgPayloadProto->button(ButtonIdOther)->setChecked(true); - updatePayloadProtocol(ButtonIdOther); +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } _done: isUpdateInProgress = false; - - return; } void StreamConfigDialog::LoadCurrentStream() diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 4b774f7..5909e8e 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -28,11 +28,25 @@ public: private: - QButtonGroup *bgFrameType; - QButtonGroup *bgVlan; - QButtonGroup *bgL3Proto; - QButtonGroup *bgL4Proto; - QButtonGroup *bgPayloadProto; + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoPayload = 5, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; QStringListModel *mpAvailableProtocolsModel; QStringListModel *mpSelectedProtocolsModel; @@ -68,11 +82,11 @@ private slots: // "Simple" Protocol Selection related bool skipProtocols(int layer); - void updateFrameTypeProtocol(int id); - void updateVlanProtocol(int id); - void updateL3Protocol(int id); - void updateL4Protocol(int id); - void updatePayloadProtocol(int id); + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); void updateSelectProtocolsSimpleWidget(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 6e15d3b..07457e6 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,8 +8,8 @@ 0 0 - 524 - 458 + 569 + 464 @@ -173,7 +173,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 465 + 527 226 @@ -181,16 +181,58 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Simple - - + + - Frame Type + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 - None (Mac Only) + None true @@ -244,50 +286,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - + + true - - VLAN - - - false - - - false - - - - - - None - - - true - - - - - - - Single Tag - - - - - - - Double Tag - - - - - - - - L3 @@ -348,8 +351,95 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + true + L4 @@ -417,44 +507,15 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Payload - - - - - - Pattern - - - true - - - - - - - false - - - Other - - - - - - - + Qt::Vertical - 107 - 24 + 20 + 21 @@ -1084,7 +1145,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff lePktLen lePktLenMin lePktLenMax - rbFtNone rbFtEthernet2 rbFt802Dot3Raw rbFt802Dot3Llc diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 2bac712..24355f2 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -22,9 +22,12 @@ - metaFieldCount() - isMetaField() */ -AbstractProtocol::AbstractProtocol(StreamBase *stream) +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) { + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); mpStream = stream; + this->parent = parent; + prev = next = NULL; metaCount = -1; protoSize = -1; } @@ -33,7 +36,8 @@ AbstractProtocol::~AbstractProtocol() { } -AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream) +AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { return NULL; } @@ -139,6 +143,7 @@ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const FieldFrameValue, the subclass should handle and return a value for FieldBitSize to prevent endless recrusion - - protocolFrameCksum() + - protocolFramePayloadSize() */ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const @@ -184,17 +189,16 @@ quint32 AbstractProtocol::protocolId(ProtocolIdType type) const quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const { - quint32 id = 0xFFFFFFFF; - ProtocolListIterator *iter = mpStream->createProtocolListIterator(); + quint32 id; - if (iter->findNext(this)) - { - if (iter->hasNext()) - id = iter->next()->protocolId(type); - } - delete iter; + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; - qDebug("%s: payloadProtocolId = %u", __FUNCTION__, id); + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); return id; } @@ -219,17 +223,15 @@ int AbstractProtocol::protocolFrameSize() const int AbstractProtocol::protocolFrameOffset() const { int size = 0; - ProtocolListIterator *iter = mpStream->createProtocolListIterator(); - - if (iter->findNext(this)) + AbstractProtocol *p = prev; + while (p) { - iter->previous(); - while (iter->hasPrevious()) - size += iter->previous()->protocolFrameSize(); + size += p->protocolFrameSize(); + p = p->prev; } - else - return -1; - delete iter; + + if (parent) + size += parent->protocolFrameOffset(); qDebug("%s: ofs = %d", __FUNCTION__, size); return size; @@ -238,17 +240,14 @@ int AbstractProtocol::protocolFrameOffset() const int AbstractProtocol::protocolFramePayloadSize() const { int size = 0; - - ProtocolListIterator *iter = mpStream->createProtocolListIterator(); - - if (iter->findNext(this)) + AbstractProtocol *p = next; + while (p) { - while (iter->hasNext()) - size += iter->next()->protocolFrameSize(); + size += p->protocolFrameSize(); + p = p->next; } - else - return -1; - delete iter; + if (parent) + size += parent->protocolFramePayloadSize(); qDebug("%s: payloadSize = %d", __FUNCTION__, size); return size; @@ -361,7 +360,7 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { static int recursionCount = 0; - quint32 cksum = 0; + quint32 cksum = 0xFFFFFFFF; recursionCount++; Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); @@ -426,19 +425,24 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, CksumType cksumType) const { - quint32 sum = 0xFFFF; - ProtocolListIterator *iter = mpStream->createProtocolListIterator(); + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; Q_ASSERT(cksumType == CksumIpPseudo); - if (iter->findNext(this)) + while (p) { - iter->previous(); - if (iter->hasPrevious()) - sum = iter->previous()->protocolFrameCksum(streamIndex, - CksumIpPseudo); + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + p = p->prev; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; } - delete iter; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); @@ -451,21 +455,22 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, { quint32 sum = 0; quint16 cksum; - ProtocolListIterator *iter = mpStream->createProtocolListIterator(); + AbstractProtocol *p = next; Q_ASSERT(cksumType == CksumIp); - if (iter->findNext(this)) + while (p) { - while (iter->hasNext()) - { - cksum = iter->next()->protocolFrameCksum(streamIndex, CksumIp); - sum += (quint16) ~cksum; - } + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; } - else - return 0; - delete iter; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index aca2d2d..13db703 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -20,16 +20,24 @@ QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) class StreamBase; +class ProtocolListIterator; class AbstractProtocol { + template + friend class ComboProtocol; + friend class ProtocolListIterator; + private: mutable int metaCount; mutable int protoSize; mutable QString protoAbbr; protected: - StreamBase *mpStream; + StreamBase *mpStream; + AbstractProtocol *parent; + AbstractProtocol *prev; + AbstractProtocol *next; public: enum FieldFlag { @@ -61,10 +69,11 @@ public: CksumMax }; - AbstractProtocol(StreamBase *stream); + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~AbstractProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; diff --git a/common/comboprotocol.h b/common/comboprotocol.h index 62828cb..34cba86 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -3,7 +3,7 @@ #include "abstractprotocol.h" -template +template class ComboProtocol : public AbstractProtocol { private: @@ -12,36 +12,67 @@ private: QWidget *configForm; public: - ComboProtocol(StreamBase *stream) + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) { - protoA = new ProtoA(stream); - protoB = new ProtoB(stream); + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; configForm = NULL; - } - virtual ~ComboProtocol() - { - delete protoA; - delete protoB; - delete configForm; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); } - static ComboProtocol* createInstance(StreamBase *stream) + virtual ~ComboProtocol() { - return new ComboProtocol(stream); + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); } virtual quint32 protocolNumber() const { - return 0; //FIXME + return protoNumber; } virtual void protoDataCopyInto(OstProto::Protocol &protocol) const { - // FIXME + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) { - // FIXME + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we make a copy first + + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } } virtual QString name() const @@ -68,26 +99,32 @@ public: virtual FieldFlags fieldFlags(int index) const { - if (index < protoA->fieldCount()) + int cnt = protoA->fieldCount(); + + if (index < cnt) return protoA->fieldFlags(index); else - return protoB->fieldFlags(index); + return protoB->fieldFlags(index - cnt); } virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const { - if (index < protoA->fieldCount()) - return protoA->fieldData(index); + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); else - return protoB->fieldData(index); + return protoB->fieldData(index - cnt, attrib, streamIndex); } virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue) { - if (index < protoA->fieldCount()) - return protoA->fieldData(index); + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); else - return protoB->fieldData(index); + return protoB->setFieldData(index - cnt, value, attrib); } #if 0 @@ -109,11 +146,13 @@ public: { if (configForm == NULL) { - QVBoxLayout *layout = new VBoxLayout; + QVBoxLayout *layout = new QVBoxLayout; configForm = new QWidget; layout->addWidget(protoA->configWidget()); layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); configForm->setLayout(layout); } return configForm; diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..1553549 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,11 @@ +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..3aa1ca4 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,14 @@ +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 127; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..3eee505 --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,11 @@ +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..b9a03f5 --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,12 @@ +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 128; +} diff --git a/common/dot3.cpp b/common/dot3.cpp index f020b6e..cae0012 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -12,8 +12,8 @@ Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) setupUi(this); } -Dot3Protocol::Dot3Protocol(StreamBase *stream) - : AbstractProtocol(stream) +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -23,9 +23,10 @@ Dot3Protocol::~Dot3Protocol() delete configForm; } -AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream) +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new Dot3Protocol(stream); + return new Dot3Protocol(stream, parent); } quint32 Dot3Protocol::protocolNumber() const @@ -75,14 +76,16 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, { quint16 len; - len = mpStream->frameLen() - SZ_FCS; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(); return len; } case FieldTextValue: { quint16 len; - len = mpStream->frameLen() - SZ_FCS; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(); return QString("%1").arg(len); } case FieldFrameValue: @@ -90,11 +93,14 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, quint16 len; QByteArray fv; - len = mpStream->frameLen() - SZ_FCS; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(); fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; } + case FieldBitSize: + return 16; default: break; } diff --git a/common/dot3.h b/common/dot3.h index 268e61d..def1031 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -26,10 +26,11 @@ private: }; public: - Dot3Protocol(StreamBase *stream); + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Dot3Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/eth2.cpp b/common/eth2.cpp index d1c4b9c..7a6f7b5 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -9,8 +9,8 @@ Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) setupUi(this); } -Eth2Protocol::Eth2Protocol(StreamBase *stream) - : AbstractProtocol(stream) +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ Eth2Protocol::~Eth2Protocol() delete configForm; } -AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream) +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new Eth2Protocol(stream); + return new Eth2Protocol(stream, parent); } quint32 Eth2Protocol::protocolNumber() const diff --git a/common/eth2.h b/common/eth2.h index a99efbf..0555d65 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -26,10 +26,11 @@ private: }; public: - Eth2Protocol(StreamBase *stream); + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Eth2Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/ip4.cpp b/common/ip4.cpp index 296f09b..4fd594b 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -49,8 +49,8 @@ void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) } } -Ip4Protocol::Ip4Protocol(StreamBase *stream) - : AbstractProtocol(stream) +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -60,9 +60,10 @@ Ip4Protocol::~Ip4Protocol() delete configForm; } -AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream) +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new Ip4Protocol(stream); + return new Ip4Protocol(stream, parent); } quint32 Ip4Protocol::protocolNumber() const @@ -592,7 +593,7 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, // Above calculation done assuming 'big endian' // - so convert to host order //return qFromBigEndian(sum); - return sum; + return ~sum; } default: break; diff --git a/common/ip4.h b/common/ip4.h index a539e71..e378755 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -59,10 +59,11 @@ private: }; public: - Ip4Protocol(StreamBase *stream); + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Ip4Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/llc.cpp b/common/llc.cpp index 8fe57af..b352cab 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -9,8 +9,8 @@ LlcConfigForm::LlcConfigForm(QWidget *parent) setupUi(this); } -LlcProtocol::LlcProtocol(StreamBase *stream) - : AbstractProtocol(stream) +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ LlcProtocol::~LlcProtocol() delete configForm; } -AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream) +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new LlcProtocol(stream); + return new LlcProtocol(stream, parent); } quint32 LlcProtocol::protocolNumber() const diff --git a/common/llc.h b/common/llc.h index 9fe5d28..3555e92 100644 --- a/common/llc.h +++ b/common/llc.h @@ -31,10 +31,11 @@ private: }; public: - LlcProtocol(StreamBase *stream); + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~LlcProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/mac.cpp b/common/mac.cpp index 7f87a5b..ae80905 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -49,8 +49,8 @@ void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) } -MacProtocol::MacProtocol(StreamBase *stream) - : AbstractProtocol(stream) +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -60,9 +60,10 @@ MacProtocol::~MacProtocol() delete configForm; } -AbstractProtocol* MacProtocol::createInstance(StreamBase *stream) +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) { - return new MacProtocol(stream); + return new MacProtocol(stream, parent); } quint32 MacProtocol::protocolNumber() const diff --git a/common/mac.h b/common/mac.h index a71e7cc..a493bd6 100644 --- a/common/mac.h +++ b/common/mac.h @@ -40,10 +40,11 @@ private: }; public: - MacProtocol(StreamBase *stream); + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~MacProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/ostproto.pro b/common/ostproto.pro index 0e095a1..9945212 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -22,7 +22,10 @@ PROTOS += \ dot3.proto \ llc.proto \ snap.proto \ + dot2llc.proto \ + dot2snap.proto \ vlan.proto \ + vlanstack.proto \ ip4.proto \ tcp.proto \ udp.proto @@ -39,7 +42,10 @@ HEADERS += \ dot3.h \ llc.h \ snap.h \ + dot2llc.h \ + dot2snap.h \ vlan.h \ + vlanstack.h \ ip4.h \ tcp.h \ udp.h diff --git a/common/payload.cpp b/common/payload.cpp index 6c93cb3..a83817c 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -30,8 +30,8 @@ void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) } } -PayloadProtocol::PayloadProtocol(StreamBase *stream) - : AbstractProtocol(stream) +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -41,9 +41,10 @@ PayloadProtocol::~PayloadProtocol() delete configForm; } -AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream) +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new PayloadProtocol(stream); + return new PayloadProtocol(stream, parent); } quint32 PayloadProtocol::protocolNumber() const diff --git a/common/payload.h b/common/payload.h index 46c1d70..cb93006 100644 --- a/common/payload.h +++ b/common/payload.h @@ -31,10 +31,11 @@ private: }; public: - PayloadProtocol(StreamBase *stream); + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~PayloadProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/protocol.proto b/common/protocol.proto index 9788735..372ad54 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -81,8 +81,12 @@ message Protocol { kLlcFieldNumber = 123; kSnapFieldNumber = 124; + kVlanStackFieldNumber = 125; kVlanFieldNumber = 126; + kDot2LlcFieldNumber = 127; + kDot2SnapFieldNumber = 128; + kIp4FieldNumber = 130; kArpFieldNumber = 131; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp index 20a08ec..268e2d6 100644 --- a/common/protocollistiterator.cpp +++ b/common/protocollistiterator.cpp @@ -1,5 +1,6 @@ #include "protocollistiterator.h" #include "protocollist.h" +#include "abstractprotocol.h" ProtocolListIterator::ProtocolListIterator(ProtocolList &list) { @@ -31,8 +32,24 @@ bool ProtocolListIterator::hasPrevious() const return _iter->hasPrevious(); } -void ProtocolListIterator::insert(const AbstractProtocol* value) +void ProtocolListIterator::insert(AbstractProtocol* value) { + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + _iter->insert((AbstractProtocol*)((uint)value)); } @@ -58,11 +75,21 @@ AbstractProtocol* ProtocolListIterator::previous() void ProtocolListIterator::remove() { + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; _iter->remove(); } -void ProtocolListIterator::setValue(const AbstractProtocol* value) const +void ProtocolListIterator::setValue(AbstractProtocol* value) const { + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; _iter->setValue((AbstractProtocol*)((uint)value)); } diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h index c2dffd3..8ad4168 100644 --- a/common/protocollistiterator.h +++ b/common/protocollistiterator.h @@ -15,13 +15,13 @@ public: bool findPrevious(const AbstractProtocol* value); bool hasNext() const; bool hasPrevious() const; - void insert(const AbstractProtocol* value); + void insert(AbstractProtocol* value); AbstractProtocol* next(); AbstractProtocol* peekNext() const; AbstractProtocol* peekPrevious() const; AbstractProtocol* previous(); void remove(); - void setValue(const AbstractProtocol* value) const; + void setValue(AbstractProtocol* value) const; void toBack(); void toFront(); const AbstractProtocol* value() const; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index a353796..9010354 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -11,7 +11,10 @@ #include "dot3.h" #include "llc.h" #include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" #include "vlan.h" +#include "vlanstack.h" #include "ip4.h" #include "tcp.h" #include "udp.h" @@ -20,16 +23,32 @@ ProtocolManager OstProtocolManager; ProtocolManager::ProtocolManager() { - registerProtocol(51, QString("mac"), (void*) MacProtocol::createInstance); - registerProtocol(52, QString("payload"), (void*) PayloadProtocol::createInstance); - registerProtocol(121, QString("eth2"), (void*) Eth2Protocol::createInstance); - registerProtocol(122, QString("dot3"), (void*) Dot3Protocol::createInstance); - registerProtocol(123, QString("llc"), (void*) LlcProtocol::createInstance); - registerProtocol(124, QString("snap"), (void*) SnapProtocol::createInstance); - registerProtocol(126, QString("vlan"), (void*) VlanProtocol::createInstance); - registerProtocol(130, QString("ip4"), (void*) Ip4Protocol::createInstance); - registerProtocol(140, QString("tcp"), (void*) TcpProtocol::createInstance); - registerProtocol(141, QString("udp"), (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMacFieldNumber, + QString("mac"), (void*) MacProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + QString("payload"), (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + QString("eth2"), (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + QString("dot3"), (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + QString("llc"), (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + QString("snap"), (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + QString("dot2Llc"), (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + QString("dot2Snap"), (void*) Dot2SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + QString("vlan"), (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + QString("vlanstack"), (void*) VlanStackProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + QString("ip4"), (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + QString("tcp"), (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + QString("udp"), (void*) UdpProtocol::createInstance); } void ProtocolManager::registerProtocol(int protoNumber, QString protoName, @@ -42,25 +61,25 @@ void ProtocolManager::registerProtocol(int protoNumber, QString protoName, } AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, - StreamBase *stream) + StreamBase *stream, AbstractProtocol *parent) { - AbstractProtocol* (*pc)(StreamBase*); + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); AbstractProtocol* p; - pc = (AbstractProtocol* (*)(StreamBase*)) + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) factory.value(protoNumber); Q_ASSERT(pc != NULL); - p = (*pc)(stream); + p = (*pc)(stream, parent); return p; } AbstractProtocol* ProtocolManager::createProtocol(QString protoName, - StreamBase *stream) + StreamBase *stream, AbstractProtocol *parent) { - return createProtocol(nameToNumberMap.value(protoName), stream); + return createProtocol(nameToNumberMap.value(protoName), stream, parent); } QStringList ProtocolManager::protocolDatabase() diff --git a/common/protocolmanager.h b/common/protocolmanager.h index ef0a605..985253b 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -19,8 +19,10 @@ public: void registerProtocol(int protoNumber, QString protoName, void *protoCreator); - AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream); - AbstractProtocol* createProtocol(QString protoName, StreamBase *stream); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); QStringList protocolDatabase(); }; diff --git a/common/sample.cpp b/common/sample.cpp index 9dbb81e..3076e6c 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -9,8 +9,8 @@ SampleConfigForm::SampleConfigForm(QWidget *parent) setupUi(this); } -SampleProtocol::SampleProtocol(StreamBase *stream); - : AbstractProtocol(stream) +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent); + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ SampleProtocol::~SampleProtocol() delete configForm; } -AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream) +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new SampleProtocol(frameProtoList, streamCore); + return new SampleProtocol(stream, parent); } quint32 SampleProtocol::protocolNumber() const diff --git a/common/sample.h b/common/sample.h index e02b6bc..6075262 100644 --- a/common/sample.h +++ b/common/sample.h @@ -26,10 +26,11 @@ private: }; public: - SampleProtocol(StreamBase *stream); + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SampleProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/snap.cpp b/common/snap.cpp index 26f5c6c..c651bb1 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -9,8 +9,8 @@ SnapConfigForm::SnapConfigForm(QWidget *parent) setupUi(this); } -SnapProtocol::SnapProtocol(StreamBase *stream) - : AbstractProtocol(stream) +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ SnapProtocol::~SnapProtocol() delete configForm; } -AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream) +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new SnapProtocol(stream); + return new SnapProtocol(stream, parent); } quint32 SnapProtocol::protocolNumber() const diff --git a/common/snap.h b/common/snap.h index 79e2780..a2531f6 100644 --- a/common/snap.h +++ b/common/snap.h @@ -27,10 +27,11 @@ private: }; public: - SnapProtocol(StreamBase *stream); + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SnapProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/streambase.cpp b/common/streambase.cpp index b98c6c6..ecfc07c 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -12,22 +12,23 @@ StreamBase::StreamBase() : mControl(new OstProto::StreamControl) { AbstractProtocol *proto; + ProtocolListIterator *iter; mStreamId->set_id(0xFFFFFFFF); currentFrameProtocols = new ProtocolList; + iter = createProtocolListIterator(); // By default newly created streams have the mac and payload protocols proto = OstProtocolManager.createProtocol("mac", this); - currentFrameProtocols->append(proto); + iter->insert(proto); qDebug("stream: mac = %p", proto); proto = OstProtocolManager.createProtocol("payload", this); - currentFrameProtocols->append(proto); + iter->insert(proto); qDebug("stream: payload = %p", proto); { - ProtocolListIterator *iter = createProtocolListIterator(); iter->toFront(); while (iter->hasNext()) { @@ -40,8 +41,9 @@ StreamBase::StreamBase() : qDebug("{[%d]}", iter->next()->protocolNumber()); // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); } - delete iter; } + + delete iter; } StreamBase::~StreamBase() @@ -53,20 +55,24 @@ StreamBase::~StreamBase() void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) { - AbstractProtocol *proto; + AbstractProtocol *proto; + ProtocolListIterator *iter; mStreamId->CopyFrom(stream.stream_id()); mCore->CopyFrom(stream.core()); mControl->CopyFrom(stream.control()); currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); for (int i=0; i < stream.protocol_size(); i++) { proto = OstProtocolManager.createProtocol( stream.protocol(i).protocol_id().id(), this); proto->protoDataCopyFrom(stream.protocol(i)); - currentFrameProtocols->append(proto); + iter->insert(proto); } + + delete iter; } void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const diff --git a/common/tcp.cpp b/common/tcp.cpp index 279efc1..dc461b6 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -9,8 +9,8 @@ TcpConfigForm::TcpConfigForm(QWidget *parent) setupUi(this); } -TcpProtocol::TcpProtocol(StreamBase *stream) - : AbstractProtocol(stream) +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ TcpProtocol::~TcpProtocol() delete configForm; } -AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream) +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new TcpProtocol(stream); + return new TcpProtocol(stream, parent); } quint32 TcpProtocol::protocolNumber() const diff --git a/common/tcp.h b/common/tcp.h index 36edba8..cfd43a6 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -45,10 +45,11 @@ private: }; public: - TcpProtocol(StreamBase *stream); + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~TcpProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/udp.cpp b/common/udp.cpp index 7564d2a..c722b54 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -9,8 +9,8 @@ UdpConfigForm::UdpConfigForm(QWidget *parent) setupUi(this); } -UdpProtocol::UdpProtocol(StreamBase *stream) - : AbstractProtocol(stream) +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -20,9 +20,10 @@ UdpProtocol::~UdpProtocol() delete configForm; } -AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream) +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new UdpProtocol(stream); + return new UdpProtocol(stream, parent); } quint32 UdpProtocol::protocolNumber() const diff --git a/common/udp.h b/common/udp.h index a2abbed..278809a 100644 --- a/common/udp.h +++ b/common/udp.h @@ -32,10 +32,11 @@ private: }; public: - UdpProtocol(StreamBase *stream); + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~UdpProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/vlan.cpp b/common/vlan.cpp index 2f60601..49b9a27 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -8,8 +8,8 @@ VlanConfigForm::VlanConfigForm(QWidget *parent) setupUi(this); } -VlanProtocol::VlanProtocol(StreamBase *stream) - : AbstractProtocol(stream) +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) { configForm = NULL; } @@ -19,9 +19,10 @@ VlanProtocol::~VlanProtocol() delete configForm; } -AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream) +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) { - return new VlanProtocol(stream); + return new VlanProtocol(stream, parent); } quint32 VlanProtocol::protocolNumber() const diff --git a/common/vlan.h b/common/vlan.h index fedf315..c5655da 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -32,10 +32,11 @@ private: }; public: - VlanProtocol(StreamBase *stream); + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~VlanProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream); + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..5202729 --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,10 @@ +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..4c3d06d --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,12 @@ +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 125; +} diff --git a/server/myservice.cpp b/server/myservice.cpp index 6d9d2ae..ff01e17 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -2,6 +2,7 @@ #include #include #include "qdebug.h" +#include #include "myservice.h" #include "../common/protocollist.h" @@ -86,7 +87,7 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) // ------------------ PortInfo -------------------- // PortInfo::PortInfo(uint id, pcap_if_t *dev) - : monitorRx(this), monitorTx(this), transmitter(this) + : monitorRx(this), monitorTx(this), transmitter(this), capturer(this) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -333,6 +334,21 @@ void PortInfo::stopTransmit() transmitter.stop(); } +void PortInfo::startCapture() +{ + capturer.start(); +} + +void PortInfo::stopCapture() +{ + capturer.stop(); +} + +void PortInfo::viewCapture() +{ + capturer.view(); +} + void PortInfo::resetStats() { memcpy((void*) &epochStats, (void*) &stats, sizeof(stats)); @@ -714,6 +730,59 @@ void PortInfo::PortTransmitter::stop() m_stop = 1; } +/*--------------- PortCapture ---------------*/ + +PortInfo::PortCapture::PortCapture(PortInfo *port) +{ + this->port = port; + capHandle = NULL; + dumpHandle = NULL; +} + +void PortInfo::PortCapture::run() +{ + if (capHandle == NULL) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + capHandle = pcap_open_live(port->dev->name, 0, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /*ms*/, errbuf); + if (capHandle == NULL) + { + qDebug("Error opening port %s: %s\n", + port->dev->name, pcap_geterr(capHandle)); + } + } + if (!capFile.isOpen()) + { + if (!capFile.open()) + qFatal("Unable to open temp cap file"); + qDebug("cap file = %s", capFile.fileName().toAscii().constData()); + } + dumpHandle = pcap_dump_open(capHandle, + capFile.fileName().toAscii().constData()); + + pcap_loop(capHandle, -1, pcap_dump, (uchar*) dumpHandle); +} + +void PortInfo::PortCapture::stop() +{ + pcap_breakloop(capHandle); + if (dumpHandle) + { + pcap_dump_flush(dumpHandle); + pcap_dump_close(dumpHandle); + dumpHandle = NULL; + } +} + +void PortInfo::PortCapture::view() +{ + // FIXME: hack - when correcting this remove the include also + QProcess::execute("C:/Program Files/Wireshark/wireshark.exe", + QStringList() << capFile.fileName()); +} + /*--------------- MyService ---------------*/ @@ -1067,7 +1136,18 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // TODO(LOW): partial RPC? + + portInfo[portIdx]->startCapture(); + } + done->Run(); } @@ -1077,7 +1157,17 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); - controller->SetFailed("Not Implemented"); + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // TODO(LOW): partial RPC? + + portInfo[portIdx]->stopCapture(); + } + done->Run(); } @@ -1087,6 +1177,19 @@ const ::OstProto::PortIdList* request, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); + + // FIXME: BAD BAD VERY BAD !!!!!! + for (int i=0; i < request->port_id_size(); i++) + { + uint portIdx; + + portIdx = request->port_id(i).id(); + if (portIdx >= numPorts) + continue; // TODO(LOW): partial RPC? + + portInfo[portIdx]->viewCapture(); + } + controller->SetFailed("Not Implemented"); done->Run(); } diff --git a/server/myservice.h b/server/myservice.h index 7a4f331..270bfba 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "../rpc/pbhelper.h" #include "pcapextra.h" @@ -92,6 +93,22 @@ class PortInfo void run(); void stop(); }; + + class PortCapture: public QThread + { + friend class PortInfo; + + PortInfo *port; + pcap_t *capHandle; + pcap_dumper_t *dumpHandle; + QTemporaryFile capFile; + + public: + PortCapture(PortInfo *port); + void run(); + void stop(); + void view(); + }; OstProto::Port d; @@ -136,6 +153,7 @@ class PortInfo PortMonitorRx monitorRx; PortMonitorTx monitorTx; PortTransmitter transmitter; + PortCapture capturer; struct PortStats epochStats; struct PortStats stats; @@ -153,6 +171,9 @@ public: void update(); void startTransmit(); void stopTransmit(); + void startCapture(); + void stopCapture(); + void viewCapture(); void resetStats(); }; From 84c7fe1e060964cdbd5cecc9675f723c3ed1e5c5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 3 Nov 2009 14:02:09 +0000 Subject: [PATCH 027/294] Features - Added support for retrieving the packet capture buffer from server to client (does not work consistently however - needs investigation) - getCaptureBuffer() Rpc signature changed - RPC: Added support in Rpc Channel (client) to queue calls - RPC: Added support for transferring arbitrary binary data from server to client (used to get packet capture files) - Rpc header changed - length is now 4 bytes instead of 2; there is no rsvd field any longer Fixes - RPC: Fix for the case when a msg is not received all at once over the socket - StreamConfigDialog: fixed display issue in packet view for combo protocols containing meta fields - Fixed issue with Stacked Vlan not retaining data for both CVlan and SVlan - Fixed incorrect payload size issue with increment/decrement frame length modes Refactoring, Cleanup etc. - RPC: Minor code and TODOs cleanup - Server: Minor code and TODOs cleanup - Server: Removed unused file(s): rxtx.cpp, rxtx.h - Server: Replaced direct use of ProtocolList with the ProtocolListIterator - Common: Minor code and TODOs cleanup - StreamBase::frameLen() now returns the length based on the mode/min/max and the passed in streamIndex - AbstractProtocol interface changed for methods - protocolFrameSize(), protocolFrameOffset(), protocolFramePayloadSize() : all of them now take streamIndex as an optional param with 0 as the default value - Protocols implementing the above methods changed accordingly --- client/packetmodel.cpp | 26 +- client/portgroup.cpp | 129 +++++---- client/portgroup.h | 7 +- common/abstractprotocol.cpp | 33 ++- common/abstractprotocol.h | 6 +- common/dot3.cpp | 7 +- common/ip4.cpp | 7 +- common/ip4.proto | 3 +- common/llc.cpp | 1 - common/mac.cpp | 1 - common/ostproto.pro | 3 + common/payload.cpp | 17 +- common/payload.h | 2 +- common/protocol.proto | 14 +- common/protocolmanager.cpp | 12 +- common/sample.cpp | 22 +- common/snap.cpp | 1 - common/streambase.cpp | 35 ++- common/streambase.h | 6 +- common/svlan.cpp | 49 ++++ common/svlan.h | 23 ++ common/svlan.proto | 8 + common/tcp.cpp | 1 - common/udp.cpp | 8 +- common/vlan.cpp | 1 - common/vlan.h | 4 +- common/vlanstack.h | 3 +- common/vlanstack.proto | 2 +- rpc/pbhelper.h | 3 + rpc/pbrpcchannel.cpp | 216 ++++++++++----- rpc/pbrpcchannel.h | 15 +- rpc/pbrpccommon.h | 17 +- rpc/pbrpccontroller.h | 13 +- rpc/rpcserver.cpp | 110 +++++--- rpc/rpcserver.h | 2 +- server/drone.cpp | 79 +----- server/drone.h | 23 +- server/drone_main.cpp | 4 - server/myservice.cpp | 185 +++++++------ server/myservice.h | 9 +- server/pcapextra.cpp | 4 +- server/rxtx.cpp | 514 ------------------------------------ server/rxtx.h | 84 ------ 43 files changed, 664 insertions(+), 1045 deletions(-) create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto delete mode 100644 server/rxtx.cpp delete mode 100644 server/rxtx.h diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 908de63..6b7b834 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -132,13 +132,29 @@ _exit: QVariant PacketModel::data(const QModelIndex &index, int role) const { - IndexId id; + IndexId id; + int fieldIdx = 0; if (!index.isValid()) return QVariant(); id.w = index.internalId(); + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (!(p->fieldFlags(fieldIdx).testFlag( + AbstractProtocol::FieldIsMeta))) + n--; + fieldIdx++; + } + fieldIdx--; + } + // FIXME(HI): Relook at this completely if (role == Qt::UserRole) { @@ -151,7 +167,7 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const case ITYP_FIELD: return mSelectedProtocols.at(id.ws.protocol)->fieldData( - index.row(), AbstractProtocol::FieldFrameValue); + fieldIdx, AbstractProtocol::FieldFrameValue); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); @@ -170,7 +186,7 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const case ITYP_FIELD: return mSelectedProtocols.at(id.ws.protocol)->fieldData( - index.row(), AbstractProtocol::FieldBitSize); + fieldIdx, AbstractProtocol::FieldBitSize); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); @@ -189,9 +205,9 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const .arg(mSelectedProtocols.at(id.ws.protocol)->name()); case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData(index.row(), + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, AbstractProtocol::FieldName).toString() + QString(" : ") + - mSelectedProtocols.at(id.ws.protocol)->fieldData(index.row(), + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, AbstractProtocol::FieldTextValue).toString(); default: diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 4614528..76888f3 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,6 +1,8 @@ +#include +#include + #include "portgroup.h" -#include quint32 PortGroup::mPortGroupAllocId = 0; @@ -10,7 +12,15 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) mPortGroupId = PortGroup::mPortGroupAllocId++; rpcChannel = new PbRpcChannel(ip, port); - rpcController = new PbRpcController(); + + /*! + \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation + as implemented currently, if a RPC is invoked before the previous completes, + rpc controller is overwritten due to the Reset() call - maybe we need to pass the + pointer to the controller to the callback function also? + */ + rpcController = new PbRpcController; + rpcControllerStats = new PbRpcController; serviceStub = new OstProto::OstService::Stub(rpcChannel, OstProto::OstService::STUB_OWNS_CHANNEL); @@ -458,21 +468,21 @@ void PortGroup::stopTx(QList *portList) qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) - return; + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; ack = new OstProto::Ack; - if (portList == NULL) - goto _exit; - else + + for (int i = 0; i < portList->size(); i++) { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); } + rpcController->Reset(); serviceStub->stopTx(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processStopTxAck, ack)); _exit: @@ -489,19 +499,19 @@ void PortGroup::startCapture(QList *portList) if (state() != QAbstractSocket::ConnectedState) return; - ack = new OstProto::Ack; - if (portList == NULL) + if ((portList == NULL) || (portList->size() == 0)) goto _exit; - else + + ack = new OstProto::Ack; + + for (int i = 0; i < portList->size(); i++) { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); } + rpcController->Reset(); serviceStub->startCapture(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processStartCaptureAck, ack)); _exit: @@ -518,19 +528,18 @@ void PortGroup::stopCapture(QList *portList) if (state() != QAbstractSocket::ConnectedState) return; - ack = new OstProto::Ack; - if (portList == NULL) + if ((portList == NULL) || (portList->size() == 0)) goto _exit; - else + + ack = new OstProto::Ack; + for (int i = 0; i < portList->size(); i++) { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); } + rpcController->Reset(); serviceStub->stopCapture(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processStopCaptureAck, ack)); _exit: @@ -539,29 +548,38 @@ _exit: void PortGroup::viewCapture(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::CaptureBufferList *bufList; + static QTemporaryFile *capFile = NULL; qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) - return; - - bufList = new OstProto::CaptureBufferList; - if (portList == NULL) goto _exit; - else - { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - } - serviceStub->getCaptureBuffer(rpcController, &portIdList, bufList, - NewCallback(this, &PortGroup::processViewCaptureAck, bufList)); + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + if (capFile) + delete capFile; + + /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is + not working - it fails everytime */ + capFile = new QTemporaryFile(); + capFile->open(); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId portId; + OstProto::CaptureBuffer *buf; + + portId.set_id(portList->at(i)); + + buf = new OstProto::CaptureBuffer; + rpcController->Reset(); + rpcController->setBinaryBlob(capFile); + serviceStub->getCaptureBuffer(rpcController, &portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); + } _exit: return; } @@ -594,11 +612,18 @@ void PortGroup::processStopCaptureAck(OstProto::Ack *ack) delete ack; } -void PortGroup::processViewCaptureAck(OstProto::CaptureBufferList *bufList) +void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) { qDebug("In %s", __FUNCTION__); - delete bufList; + capFile->flush(); + capFile->close(); + + if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", + QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + + delete buf; } void PortGroup::getPortStats() @@ -611,7 +636,8 @@ void PortGroup::getPortStats() return; portStatsList = new OstProto::PortStatsList; - serviceStub->getStats(rpcController, &portIdList, portStatsList, + rpcControllerStats->Reset(); + serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); } @@ -619,7 +645,7 @@ void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) { //qDebug("In %s", __FUNCTION__); - if (rpcController->Failed()) + if (rpcControllerStats->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); goto _error_exit; @@ -664,6 +690,7 @@ void PortGroup::clearPortStats(QList *portList) } } + rpcController->Reset(); serviceStub->clearStats(rpcController, &portIdList, ack, NewCallback(this, &PortGroup::processClearStatsAck, ack)); } diff --git a/client/portgroup.h b/client/portgroup.h index c297674..c74640a 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -17,6 +17,8 @@ LOW #define DEFAULT_SERVER_PORT 7878 +class QFile; + class PortGroup : public QObject { Q_OBJECT @@ -30,7 +32,8 @@ private: quint16 mServerPort; #endif PbRpcChannel *rpcChannel; - ::google::protobuf::RpcController *rpcController; + PbRpcController *rpcController; + PbRpcController *rpcControllerStats; ::OstProto::OstService::Stub *serviceStub; ::OstProto::PortIdList portIdList; @@ -80,7 +83,7 @@ public: void stopCapture(QList *portList = NULL); void processStopCaptureAck(OstProto::Ack *ack); void viewCapture(QList *portList = NULL); - void processViewCaptureAck(OstProto::CaptureBufferList *bufList); + void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); void getPortStats(); void processPortStatsList(OstProto::PortStatsList *portStatsList); diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 24355f2..10678fb 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -7,7 +7,7 @@ /*! \class AbstractProtocol - // FIXME - update this text + \todo (MED) update AbstractProtocol documentation Bare Minimum set of methods that a subclass needs to reimplement - protoDataCopyInto() [pure virtual] - protoDataCopyFrom() [pure virtual] @@ -58,7 +58,7 @@ quint32 AbstractProtocol::protocolNumber() const /* \fn virtual void protoDataCopyFrom(const OstProto::OstProto::StreamCore &stream) = 0; - FIXME */ + */ /*! Returns the full name of the protocol \n The default implementation returns a null string */ @@ -137,7 +137,18 @@ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const The FieldTextValue attribute may include additional information about the field's value e.g. a checksum field may include "(correct)" or "(incorrect)" alongwith the actual checksum value. \n - The default implementation returns FIXME + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes \note If a subclass uses any of the below functions to derive FieldFrameValue, the subclass should handle and return a value for @@ -202,7 +213,7 @@ quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const return id; } -int AbstractProtocol::protocolFrameSize() const +int AbstractProtocol::protocolFrameSize(int streamIndex) const { if (protoSize < 0) { @@ -211,7 +222,7 @@ int AbstractProtocol::protocolFrameSize() const for (int i = 0; i < fieldCount(); i++) { if (!fieldFlags(i).testFlag(FieldIsMeta)) - bitsize += fieldData(i, FieldBitSize).toUInt(); + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); } protoSize = (bitsize+7)/8; } @@ -220,34 +231,34 @@ int AbstractProtocol::protocolFrameSize() const return protoSize; } -int AbstractProtocol::protocolFrameOffset() const +int AbstractProtocol::protocolFrameOffset(int streamIndex) const { int size = 0; AbstractProtocol *p = prev; while (p) { - size += p->protocolFrameSize(); + size += p->protocolFrameSize(streamIndex); p = p->prev; } if (parent) - size += parent->protocolFrameOffset(); + size += parent->protocolFrameOffset(streamIndex); qDebug("%s: ofs = %d", __FUNCTION__, size); return size; } -int AbstractProtocol::protocolFramePayloadSize() const +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const { int size = 0; AbstractProtocol *p = next; while (p) { - size += p->protocolFrameSize(); + size += p->protocolFrameSize(streamIndex); p = p->next; } if (parent) - size += parent->protocolFramePayloadSize(); + size += parent->protocolFramePayloadSize(streamIndex); qDebug("%s: payloadSize = %d", __FUNCTION__, size); return size; diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 13db703..3e1dc12 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -97,9 +97,9 @@ public: QByteArray protocolFrameValue(int streamIndex = 0, bool forCksum = false) const; - virtual int protocolFrameSize() const; - int protocolFrameOffset() const; - int protocolFramePayloadSize() const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; diff --git a/common/dot3.cpp b/common/dot3.cpp index cae0012..0a88d50 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -77,7 +77,7 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, quint16 len; //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(); + len = protocolFramePayloadSize(streamIndex); return len; } case FieldTextValue: @@ -85,7 +85,7 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, quint16 len; //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(); + len = protocolFramePayloadSize(streamIndex); return QString("%1").arg(len); } case FieldFrameValue: @@ -94,7 +94,7 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(); + len = protocolFramePayloadSize(streamIndex); fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; @@ -116,7 +116,6 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, bool Dot3Protocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/ip4.cpp b/common/ip4.cpp index 4fd594b..c929872 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -236,7 +236,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, { int totlen; totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 20); + (protocolFramePayloadSize(streamIndex) + 20); return totlen; } case FieldFrameValue: @@ -244,7 +244,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; int totlen; totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 20); + (protocolFramePayloadSize(streamIndex) + 20); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; @@ -253,7 +253,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, { int totlen; totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 20); + (protocolFramePayloadSize(streamIndex) + 20); return QString("%1").arg(totlen); } case FieldBitSize: @@ -320,7 +320,6 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, case FieldFrameValue: { QByteArray fv; - // FIXME need to shift for 13 bits fv.resize(2); qToBigEndian((quint16) (data.frag_ofs()), (uchar*) fv.data()); diff --git a/common/ip4.proto b/common/ip4.proto index c17ec75..84fcbf4 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -20,7 +20,6 @@ message Ip4 { optional uint32 tos = 6; optional uint32 totlen = 7; optional uint32 id = 8 [default = 1234]; - // TODO: rename flags to frag_flags optional uint32 flags = 9; optional uint32 frag_ofs = 10; optional uint32 ttl = 11 [default = 127]; @@ -39,7 +38,7 @@ message Ip4 { optional uint32 dst_ip_count = 20 [default = 16]; optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; - // TODO: Options + //! \todo (LOW) IPv4 Options } extend Protocol { diff --git a/common/llc.cpp b/common/llc.cpp index b352cab..e949e08 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -128,7 +128,6 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, bool LlcProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/mac.cpp b/common/mac.cpp index ae80905..8e468a3 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -236,7 +236,6 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, bool MacProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/ostproto.pro b/common/ostproto.pro index 9945212..8ae7f99 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -25,6 +25,7 @@ PROTOS += \ dot2llc.proto \ dot2snap.proto \ vlan.proto \ + svlan.proto \ vlanstack.proto \ ip4.proto \ tcp.proto \ @@ -45,6 +46,7 @@ HEADERS += \ dot2llc.h \ dot2snap.h \ vlan.h \ + svlan.h \ vlanstack.h \ ip4.h \ tcp.h \ @@ -62,6 +64,7 @@ SOURCES += \ llc.cpp \ snap.cpp \ vlan.cpp \ + svlan.cpp \ ip4.cpp \ tcp.cpp \ udp.cpp diff --git a/common/payload.cpp b/common/payload.cpp index a83817c..1125b73 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -75,9 +75,16 @@ QString PayloadProtocol::shortName() const return QString("DATA"); } -int PayloadProtocol::protocolFrameSize() const +int PayloadProtocol::protocolFrameSize(int streamIndex) const { - return (mpStream->frameLen() - protocolFrameOffset() - SZ_FCS); + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - SZ_FCS; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; } int PayloadProtocol::fieldCount() const @@ -125,8 +132,7 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; int dataLen; - dataLen = mpStream->frameLen() - protocolFrameOffset(); - dataLen -= SZ_FCS; + dataLen = protocolFrameSize(streamIndex); // FIXME: Hack! Bad! Bad! Very Bad!!! if (dataLen <= 0) @@ -149,7 +155,7 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, fv[i] = 0xFF - (i % (0xFF + 1)); break; case OstProto::Payload::e_dp_random: - //! \todo cksum will be incorrect for random pattern + //! \todo (HIGH) cksum is incorrect for random pattern for (int i = 0; i < dataLen; i++) fv[i] = qrand() % (0xFF + 1); break; @@ -178,7 +184,6 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, bool PayloadProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/payload.h b/common/payload.h index cb93006..c2d5f4d 100644 --- a/common/payload.h +++ b/common/payload.h @@ -44,7 +44,7 @@ public: virtual QString name() const; virtual QString shortName() const; - virtual int protocolFrameSize() const; + virtual int protocolFrameSize(int streamIndex = 0) const; virtual int fieldCount() const; diff --git a/common/protocol.proto b/common/protocol.proto index 372ad54..1f3c0a5 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -1,7 +1,5 @@ // stream.proto -// FIXME: Re-evaluate Tag Values - package OstProto; message StreamId { @@ -56,9 +54,6 @@ message StreamControl { optional NextWhat next = 6 [default = e_nw_goto_next]; optional uint32 packets_per_sec = 7 [default = 1]; optional uint32 bursts_per_sec = 8 [default = 1]; - - // TODO: Gaps? - } message ProtocolId { @@ -81,11 +76,12 @@ message Protocol { kLlcFieldNumber = 123; kSnapFieldNumber = 124; - kVlanStackFieldNumber = 125; + kSvlanFieldNumber = 125; kVlanFieldNumber = 126; kDot2LlcFieldNumber = 127; kDot2SnapFieldNumber = 128; + kVlanStackFieldNumber = 129; kIp4FieldNumber = 130; kArpFieldNumber = 131; @@ -111,7 +107,7 @@ message Void { } message Ack { - // TODO + //! \todo (LOW) do we need any fields in 'Ack' } message PortId { @@ -146,7 +142,7 @@ message StreamConfigList { } message CaptureBuffer { - // TODO + //! \todo (HIGH) define CaptureBuffer } message CaptureBufferList { @@ -190,7 +186,7 @@ service OstService { rpc startCapture(PortIdList) returns (Ack); rpc stopCapture(PortIdList) returns (Ack); - rpc getCaptureBuffer(PortIdList) returns (CaptureBufferList); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); rpc getStats(PortIdList) returns (PortStatsList); rpc clearStats(PortIdList) returns (Ack); diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 9010354..56c1912 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -1,12 +1,9 @@ #include "protocolmanager.h" - -// FIXME(HI): remove -#include "protocol.pb.h" #include "abstractprotocol.h" +#include "protocol.pb.h" #include "mac.h" #include "payload.h" - #include "eth2.h" #include "dot3.h" #include "llc.h" @@ -23,6 +20,9 @@ ProtocolManager OstProtocolManager; ProtocolManager::ProtocolManager() { + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ registerProtocol(OstProto::Protocol::kMacFieldNumber, QString("mac"), (void*) MacProtocol::createInstance); registerProtocol(OstProto::Protocol::kPayloadFieldNumber, @@ -39,6 +39,8 @@ ProtocolManager::ProtocolManager() QString("dot2Llc"), (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, QString("dot2Snap"), (void*) Dot2SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + QString("svlan"), (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, QString("vlan"), (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, @@ -54,7 +56,7 @@ ProtocolManager::ProtocolManager() void ProtocolManager::registerProtocol(int protoNumber, QString protoName, void *protoInstanceCreator) { - // TODO: validate incoming params for duplicates with existing + //! \todo (MED) validate incoming params for duplicates with existing nameToNumberMap.insert(protoName, protoNumber); numberToNameMap.insert(protoNumber, protoName); factory.insert(protoNumber, protoInstanceCreator); diff --git a/common/sample.cpp b/common/sample.cpp index 3076e6c..63aaa7e 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -3,6 +3,10 @@ #include "sample.h" +/*! \todo (MED) Complete the "sample" protocol and make it compilable so that + it can be used as an example for new protocols + */ + SampleConfigForm::SampleConfigForm(QWidget *parent) : QWidget(parent) { @@ -90,18 +94,18 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, { switch (index) { - case sample_FIXME: + case sample_one: { switch(attrib) { case FieldName: - return QString("FIXME"); + return QString("ONE"); case FieldValue: - return data.FIXME(); + return data.one(); case FieldTextValue: - return QString("%1").arg(data.FIXME()); + return QString("%1").arg(data.one()); case FieldFrameValue: - return QByteArray(1, (char)(data.FIXME() & 0xF0)); + return QByteArray(1, (char)(data.one() & 0xF0)); case FieldBitSize: return 4; default: @@ -110,16 +114,16 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, break; } - case sample_FIXME: + case sample_two: { switch(attrib) { case FieldName: - return QString("FIXME"); + return QString("TWO"); case FieldValue: - return FIXME; + return data.two(); case FieldTextValue: - return QString("%1").arg(FIXME); + return QString("%1").arg(data.two()); case FieldFrameValue: { QByteArray fv; diff --git a/common/snap.cpp b/common/snap.cpp index c651bb1..3dfd7e4 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -133,7 +133,6 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, bool SnapProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/streambase.cpp b/common/streambase.cpp index ecfc07c..1ed8d7f 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -169,9 +169,40 @@ bool StreamBase::setLenMode(FrameLengthMode lenMode) return true; } -quint16 StreamBase::frameLen() +quint16 StreamBase::frameLen(int streamIndex) { - return mCore->frame_len(); + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + qsrand(((uint) this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; } bool StreamBase::setFrameLen(quint16 frameLen) diff --git a/common/streambase.h b/common/streambase.h index 99838d9..878b2d7 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -17,8 +17,6 @@ private: OstProto::StreamCore *mCore; OstProto::StreamControl *mControl; -protected: - //! \todo TODO: Make ProtocolList a private member of StreamBase? ProtocolList *currentFrameProtocols; public: @@ -30,7 +28,7 @@ public: ProtocolListIterator* createProtocolListIterator(); - // TODO: make a copy constructor + //! \todo (LOW) should we have a copy constructor?? public: enum FrameLengthMode { @@ -81,7 +79,7 @@ public: FrameLengthMode lenMode(); bool setLenMode(FrameLengthMode lenMode); - quint16 frameLen(); + quint16 frameLen(int streamIndex = 0); bool setFrameLen(quint16 frameLen); quint16 frameLenMin(); diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..55ef836 --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,49 @@ +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..0fb4b97 --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,23 @@ +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..72e4557 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,8 @@ +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 125; +} diff --git a/common/tcp.cpp b/common/tcp.cpp index dc461b6..be16120 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -377,7 +377,6 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, bool TcpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/udp.cpp b/common/udp.cpp index c722b54..0d5b772 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -159,7 +159,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 8); + (protocolFramePayloadSize(streamIndex) + 8); return totlen; } case FieldFrameValue: @@ -168,7 +168,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, int totlen; totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 8); + (protocolFramePayloadSize(streamIndex) + 8); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; @@ -178,7 +178,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, int totlen; totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize() + 8); + (protocolFramePayloadSize(streamIndex) + 8); return QString("%1").arg(totlen); } case FieldBitSize: @@ -254,7 +254,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, bool UdpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME + //! implement UdpProtocol::setFieldData() return false; } diff --git a/common/vlan.cpp b/common/vlan.cpp index 49b9a27..244a473 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -196,7 +196,6 @@ QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, bool VlanProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME return false; } diff --git a/common/vlan.h b/common/vlan.h index c5655da..5e5d7de 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -16,7 +16,6 @@ public: class VlanProtocol : public AbstractProtocol { private: - OstProto::Vlan data; VlanConfigForm *configForm; enum Vlanfield { @@ -31,6 +30,9 @@ private: vlan_fieldCount }; +protected: + OstProto::Vlan data; + public: VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~VlanProtocol(); diff --git a/common/vlanstack.h b/common/vlanstack.h index 5202729..f74aaed 100644 --- a/common/vlanstack.h +++ b/common/vlanstack.h @@ -2,9 +2,10 @@ #define _VLAN_STACK_H #include "comboprotocol.h" +#include "svlan.h" #include "vlan.h" typedef ComboProtocol VlanStackProtocol; + SVlanProtocol, VlanProtocol> VlanStackProtocol; #endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto index 4c3d06d..a54f11f 100644 --- a/common/vlanstack.proto +++ b/common/vlanstack.proto @@ -8,5 +8,5 @@ message VlanStack { } extend Protocol { - optional VlanStack vlanStack = 125; + optional VlanStack vlanStack = 129; } diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index e3d85b0..7a70c75 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -6,9 +6,11 @@ #include +#if 0 // not reqd. any longer? class PbHelper { public: + // FIXME: Change msg from * to & void ForceSetSingularDefault(::google::protobuf::Message *msg) { @@ -146,3 +148,4 @@ public: } }; #endif +#endif diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index ed60d2f..9d5be05 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -1,7 +1,5 @@ #include "pbrpcchannel.h" -//#include "../common/protocol.pb.h" - PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) { isPending = false; @@ -65,17 +63,36 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char msg[4096]; // FIXME: hardcoding - char *p = (char *)&msg; + char msg[MSGBUF_SIZE]; int len; + bool ret; - //qDebug("In %s", __FUNCTION__); + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending", + method->index(), pendingMethodId); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } if (!req->IsInitialized()) { - qDebug("RpcChannel: missing required fields in request"); + qWarning("RpcChannel: missing required fields in request"); qDebug(req->InitializationErrorString().c_str()); + qFatal("exiting"); + controller->SetFailed("Required fields missing"); done->Run(); return; @@ -87,103 +104,172 @@ void PbRpcChannel::CallMethod( this->response=response; isPending = true; - *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_REQUEST); // type - //qDebug("CLi:GET16 = %d/%d, type = %d", GET16(p+0), NTOHS(GET16(p+0)), - //PB_MSG_TYPE_REQUEST); - *((quint16*)(p+2)) = HTONS(method->index()); // method id - // (p+4) len later after serialization - *((quint16*)(p+6)) = HTONS(0); // rsvd - - // SerialData is at offset 8 - req->SerializeToArray((void*) (p+8), sizeof(msg)); + ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); + Q_ASSERT(ret == true); len = req->ByteSize(); - *((quint16*)(p+4)) = HTONS(len); // len + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type + *((quint16*)(&msg[2])) = HTONS(method->index()); // method id + *((quint32*)(&msg[4])) = HTONL(len); // len // Avoid printing stats since it happens every couple of seconds if (pendingMethodId != 12) { - qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, len+8, - req->DebugString().c_str()); - BUFDUMP(msg, len+8); + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE + len); } - mpSocket->write(msg, len + 8); + mpSocket->write(msg, PB_HDR_SIZE + len); } void PbRpcChannel::on_mpSocket_readyRead() { - char msg[4096]; // FIXME: hardcoding; + char msg[MSGBUF_SIZE]; char *p = (char*)&msg; int msgLen; - quint16 type, method, len, rsvd; - PbRpcController *controller; + static bool parsing = false; + static quint16 type, method; + static quint32 len; - //qDebug("In %s", __FUNCTION__); - - msgLen = mpSocket->read(msg, sizeof(msg)); + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); - //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); - //BUFDUMP(msg, msgLen); - - type = NTOHS(GET16(p+0)); - method = NTOHS(GET16(p+2)); - len = NTOHS(GET16(p+4)); - rsvd = NTOHS(GET16(p+6)); - - if (!isPending) + if (!parsing) { - qDebug("not waiting for response"); - - goto _error_exit; + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read(msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = NTOHS(GET16(p+0)); + method = NTOHS(GET16(p+2)); + len = NTOHL(GET32(p+4)); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; } - if (type != PB_MSG_TYPE_RESPONSE) + switch (type) { - qDebug("invalid msgType %d (expected = %d)", type, - PB_MSG_TYPE_RESPONSE); - - goto _error_exit; - } + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; - if (pendingMethodId != method) - { - qDebug("invalid method id %d (expected = %d)", method, - pendingMethodId); - - goto _error_exit; - } + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; - // Serialized data starts from offset 8 - response->ParseFromArray((void*) &msg[8], len); + l = mpSocket->read(msg, sizeof(msg)); + blob->write(msg, l); + cumLen += l; + } - // Avoid printing stats - if (method != 12) - { - qDebug("client(%s): Parsed as %s", __FUNCTION__, - response->DebugString().c_str()); - } + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); - if (!response->IsInitialized()) - { - qDebug("RpcChannel: missing required fields in response"); - qDebug(response->InitializationErrorString().c_str()); + if (cumLen < len) + return; - controller->SetFailed("Required fields missing"); + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + // Wait till we have the entire message + if (mpSocket->bytesAvailable() < len) + { + qDebug("client: not enough data available for a complete msg"); + return; + } + + msgLen = mpSocket->read(msg, sizeof(msg)); + + Q_ASSERT((unsigned) msgLen == len); + + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + response->ParseFromArray((void*) msg, len); + + // Avoid printing stats + if (method != 12) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug(response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + } pendingMethodId = -1; controller = NULL; - //done = NULL; response = NULL; isPending = false; + parsing = false; done->Run(); + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + return; _error_exit: + parsing = false; qDebug("client(%s) discarding received msg", __FUNCTION__); return; } diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h index 657b095..17f287a 100644 --- a/rpc/pbrpcchannel.h +++ b/rpc/pbrpcchannel.h @@ -25,13 +25,22 @@ class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel // passed by the stub to CallMethod(). They are reset to NULL when // we get a response back from the server in on_mpSocket_readyRead() // after calling done->Run(). - // - // TODO(?): change controller, done and response to references - // instead of pointers? + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ ::google::protobuf::RpcController *controller; ::google::protobuf::Closure *done; ::google::protobuf::Message *response; + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + QHostAddress mServerAddress; quint16 mServerPort; QTcpSocket *mpSocket; diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index 5407602..ef4b1ee 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -1,7 +1,7 @@ #ifndef _PB_RPC_COMMON_H #define _PB_RPC_COMMON_H -// FIXME: check which one is right - wrong one seems to be working!!!!! +//! \todo (LOW) check which one is right - wrong one seems to be working!!!!! #if 0 #define GET16(p) (quint16)( \ (*((quint8*)(p)+0) << 8 ) \ @@ -10,6 +10,11 @@ #define GET16(p) (quint16)( \ (*((quint8*)(p)+1) << 8 ) \ | (*((quint8*)(p)+0))) +#define GET32(p) (quint32)( \ + (*((quint8*)(p)+3) << 24) \ + | (*((quint8*)(p)+2) << 16) \ + | (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) #endif #define BYTESWAP4(x) \ @@ -22,7 +27,7 @@ (((x & 0xFF00) >> 8) | \ ((x & 0x00FF) << 8)) -// TODO: portability +//! \todo (LOW) : portability #if 1 #define HTONL(x) BYTESWAP4(x) #define NTOHL(x) BYTESWAP4(x) @@ -43,10 +48,14 @@ ** RPC Header (8) ** - MSG_TYPE (2) ** - METHOD_ID (2) -** - LEN (2) [not including this header] -** - RSVD (2) +** - LEN (4) [not including this header] */ +#define PB_HDR_SIZE 8 + #define PB_MSG_TYPE_REQUEST 1 #define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#define MSGBUF_SIZE 4096 #endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 0b67ded..acc9520 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -6,22 +6,27 @@ class PbRpcController : public ::google::protobuf::RpcController { bool failed; + QIODevice *blob; std::string errStr; public: - PbRpcController() { failed = false; } + PbRpcController() { Reset(); } // Client Side Methods - void Reset() { failed=false;} + void Reset() { failed=false; blob = NULL; } bool Failed() const { return failed; } - void StartCancel() { /* TODO */} + void StartCancel() { /*! \todo (MED) */} std::string ErrorText() const { return errStr; } // Server Side Methods void SetFailed(const std::string &reason) { failed = true; errStr = reason; } bool IsCanceled() const { return false; }; - void NotifyOnCancel(::google::protobuf::Closure *callback) { /*TODO*/ } + void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; }; #endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index a3978cb..039fc83 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -1,4 +1,4 @@ -#include "pbhelper.h" +//#include "pbhelper.h" #include "rpcserver.h" RpcServer::RpcServer() @@ -37,45 +37,68 @@ bool RpcServer::registerService(::google::protobuf::Service *service, void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) { - char msg[4096]; // FIXME: hardcoding - char *p = (char *)&msg; + QIODevice *blob; + char msg[MSGBUF_SIZE]; int len; //qDebug("In RpcServer::done"); - // TODO: check PbRpcController to see if method failed if (PbRpcController->Failed()) { qDebug("rpc failed"); goto _exit; } - if (!resp->IsInitialized()) + blob = PbRpcController->binaryBlob(); + if (blob) { - qDebug("response missing required fields!!"); - qDebug(resp->InitializationErrorString().c_str()); + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + (*(quint32*)(&msg[4])) = HTONL(len); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msg)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + goto _exit; } - *((quint16*)(p+0)) = HTONS(PB_MSG_TYPE_RESPONSE); // type TODO:RESPONSE - *((quint16*)(p+2)) = HTONS(pendingMethodId); // method - *((quint16*)(p+6)) = HTONS(0); // rsvd + if (!resp->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug(resp->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } - // SerialData is at offset 8 - resp->SerializeToArray((void*) (p+8), sizeof(msg)); + resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); len = resp->ByteSize(); - (*(quint16*)(p+4)) = HTONS(len); // len + + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + *((quint32*)(&msg[4])) = HTONL(len); // len // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 12) { qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + 8, resp->DebugString().c_str()); + __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); //BUFDUMP(msg, len + 8); } - clientSock->write(msg, len + 8); + clientSock->write(msg, PB_HDR_SIZE + len); _exit: delete PbRpcController; @@ -91,7 +114,7 @@ void RpcServer::when_newConnection() LogInt(tr("already connected, no new connections will be accepted\n")); // Accept and close connection - // TODO: Send reason msg to client + //! \todo (MED) Send reason msg to client sock = server->nextPendingConnection(); sock->disconnectFromHost(); sock->deleteLater(); @@ -127,26 +150,37 @@ void RpcServer::when_error(QAbstractSocket::SocketError socketError) void RpcServer::when_dataAvail() { - char msg[4096]; // FIXME: hardcoding; + char msg[MSGBUF_SIZE]; int msgLen; - char *p = (char*) &msg; - quint16 type, method, len, rsvd; + static bool parsing = false; + static quint16 type, method; + static quint32 len; const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; PbRpcController *controller; - + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read(msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = NTOHS(GET16(&msg[0])); + method = NTOHS(GET16(&msg[2])); + len = NTOHL(GET32(&msg[4])); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (clientSock->bytesAvailable() < len) + return; + msgLen = clientSock->read(msg, sizeof(msg)); - //LogInt(QString(QByteArray(msg, msgLen).toHex())); - - //qDebug("Server %s: rcvd %d bytes", __FUNCTION__, msgLen); - //BUFDUMP(msg, msgLen); - - type = NTOHS(GET16(p+0)); - method = NTOHS(GET16(p+2)); - len = NTOHS(GET16(p+4)); - rsvd = NTOHS(GET16(p+6)); - //qDebug("type = %d, method = %d, len = %d, rsvd = %d", - //type, method, len, rsvd); + Q_ASSERT((unsigned) msgLen == len); if (type != PB_MSG_TYPE_REQUEST) { @@ -154,19 +188,18 @@ void RpcServer::when_dataAvail() type, PB_MSG_TYPE_REQUEST); goto _error_exit; } - methodDesc = service->GetDescriptor()->method(method); if (!methodDesc) { qDebug("server(%s): invalid method id %d", __FUNCTION__, method); - goto _error_exit; // TODO: Return Error to client + goto _error_exit; //! \todo Return Error to client } if (isPending) { qDebug("server(%s): rpc pending, try again", __FUNCTION__); - goto _error_exit; // TODO: Return Error to client + goto _error_exit; //! \todo Return Error to client } pendingMethodId = method; @@ -175,12 +208,12 @@ void RpcServer::when_dataAvail() req = service->GetRequestPrototype(methodDesc).New(); resp = service->GetResponsePrototype(methodDesc).New(); - // Serialized data starts from offset 8 - req->ParseFromArray((void*) (msg+8), len); + req->ParseFromArray((void*)msg, len); if (!req->IsInitialized()) { - qDebug("Missing required fields in request"); + qWarning("Missing required fields in request"); qDebug(req->InitializationErrorString().c_str()); + qFatal("exiting"); delete req; delete resp; @@ -196,9 +229,12 @@ void RpcServer::when_dataAvail() service->CallMethod(methodDesc, controller, req, resp, NewCallback(this, &RpcServer::done, resp, controller)); + parsing = false; + return; _error_exit: + parsing = false; qDebug("server(%s): discarding msg from client", __FUNCTION__); return; } diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 9e21587..d93b08a 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -27,7 +27,7 @@ class RpcServer : public QObject void LogInt (QString log) {qDebug("%s", log.toAscii().data());} public: - RpcServer(); // TODO: use 'parent' param + RpcServer(); //! \todo (LOW) use 'parent' param virtual ~RpcServer(); bool registerService(::google::protobuf::Service *service, diff --git a/server/drone.cpp b/server/drone.cpp index 98300ee..1f94d7e 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -1,33 +1,15 @@ #include "drone.h" -extern int myport; // FIXME(HIGH) +extern int myport; Drone::Drone(QDialog *parent) : QDialog(parent) { ui.setupUi(this); -#if 0 // PB - rxtx = new RxTx(this); -#endif + rpcServer = new RpcServer(); service = new MyService(this); - rpcServer->registerService(service, myport?myport:7878); - -#if 0 // PB - serverPortNum = DRONE_PORT; - clientSock = NULL; - - if (myport) - serverPortNum = myport); - - server = new QTcpServer(this); - connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); - //if (!server->listen(QHostAddress("10.0.0.1"), serverPortNum)) - if (!server->listen(QHostAddress::Any, serverPortNum)) - LogInt(tr("Unable to start the server: %1").arg(server->errorString())); - else - LogInt(tr("The server is running on %1:%2").arg(server->serverAddress().toString()).arg(server->serverPort())); -#endif + rpcServer->registerService(service, myport ? myport : 7878); } void Drone::Log(const char* str) @@ -35,62 +17,7 @@ void Drone::Log(const char* str) ui.teLog->append(QString(str)); } -#if 0 // PB -int Drone::SendMsg(const void* msg, int size) -{ - qDebug("Inside SendMsg\n"); - clientSock->write((char*) msg, size); -} -#endif - void Drone::LogInt(const QString &str) { ui.teLog->append(str); } - -#if 0 // PB -void Drone::when_newConnection() -{ - if (clientSock) - { - QTcpSocket *sock; - - LogInt(tr("already connected, no new connections will be accepted\n")); - sock = server->nextPendingConnection(); - // TODO: Send reason msg to client - sock->disconnectFromHost(); - goto _exit; - } - clientSock = server->nextPendingConnection(); - LogInt(tr("accepting new connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); - connect(clientSock, SIGNAL(readyRead()), - this, SLOT(when_dataAvail())); - connect(clientSock, SIGNAL(disconnected()), - this, SLOT(when_disconnected())); - connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(when_error(QAbstractSocket::SocketError))); - - -_exit: - return; -} - -void Drone::when_disconnected() -{ - LogInt(tr("closing connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); - clientSock->deleteLater(); - clientSock = NULL; -} - -void Drone::when_dataAvail() -{ - QByteArray msg = clientSock->read(1024); // FIXME: hardcoding - LogInt(QString(msg.toHex())); - rxtx->ProcessMsg(msg.constData(), msg.size()); -} - -void Drone::when_error(QAbstractSocket::SocketError socketError) -{ - LogInt(clientSock->errorString()); -} -#endif diff --git a/server/drone.h b/server/drone.h index 3c4adba..31574a6 100644 --- a/server/drone.h +++ b/server/drone.h @@ -3,15 +3,12 @@ #include #include + #include "ui_drone.h" #include "abstracthost.h" -#if 0 // PB -#include "rxtx.h" -#endif #include "rpcserver.h" #include "myservice.h" - class Drone : public QDialog, AbstractHost { Q_OBJECT @@ -20,28 +17,10 @@ class Drone : public QDialog, AbstractHost Ui::Drone ui; Drone(QDialog *parent = 0); void Log(const char *msg); -#if 0 // PB - int SendMsg(const void* msg, int msgLen); -#endif private: -#if 0 // PB - RxTx *rxtx; -#endif RpcServer *rpcServer; OstProto::OstService *service; void LogInt(const QString &msg); -#if 0 // PB - QTcpServer *server; - QTcpSocket *clientSock; -#define DRONE_PORT 7878 - quint16 serverPortNum; - - private slots: - void when_newConnection(); - void when_disconnected(); - void when_dataAvail(); - void when_error(QAbstractSocket::SocketError socketError); -#endif }; #endif diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 5a66b6a..007f1af 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -2,20 +2,16 @@ Drone *drone; -//void FindDevList(void); - int myport; int main(int argc, char *argv[]) { QApplication app(argc, argv); - // FIXME(HIGH) if (argc > 1) myport = atoi(argv[1]); drone = new Drone; - //FindDevList(); drone->show(); return app.exec(); } diff --git a/server/myservice.cpp b/server/myservice.cpp index ff01e17..cb9a2db 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -2,12 +2,12 @@ #include #include #include "qdebug.h" -#include #include "myservice.h" -#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" +#include "../rpc/pbrpccontroller.h" #if 0 #include @@ -19,12 +19,6 @@ StreamInfo::StreamInfo() { -#if 0 - PbHelper pbh; - - pbh.ForceSetSingularDefault(mCore); - pbh.ForceSetSingularDefault(mControl); -#endif } StreamInfo::~StreamInfo() @@ -35,30 +29,7 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) { int pktLen, len = 0; - // Decide a frame length based on length mode - switch(lenMode()) - { - case OstProto::StreamCore::e_fl_fixed: - pktLen = frameLen(); - break; - case OstProto::StreamCore::e_fl_inc: - pktLen = frameLenMin() + (n % - (frameLenMax() - frameLenMin() + 1)); - break; - case OstProto::StreamCore::e_fl_dec: - pktLen = frameLenMax() - (n % - (frameLenMax() - frameLenMin() + 1)); - break; - case OstProto::StreamCore::e_fl_random: - pktLen = frameLenMin() + (qrand() % - (frameLenMax() - frameLenMin() + 1)); - break; - default: - qWarning("Unhandled len mode %d. Using default 64", - lenMode()); - pktLen = 64; - break; - } + pktLen = frameLen(n); // pktLen is adjusted for CRC/FCS which will be added by the NIC pktLen -= 4; @@ -66,18 +37,22 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) if ((pktLen < 0) || (pktLen > bufMaxSize)) return 0; - // FIXME: Calculated pktLen is an input to Payload Protocol - foreach(const AbstractProtocol* proto, *currentFrameProtocols) - { - QByteArray ba; + ProtocolListIterator *iter; + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); ba = proto->protocolFrameValue(n); + if (len + ba.size() < bufMaxSize) - { memcpy(buf+len, ba.constData(), ba.size()); - } len += ba.size(); } + delete iter; return pktLen; } @@ -155,9 +130,9 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) if (dev->description) d.set_description(dev->description); - d.set_is_enabled(true); // FIXME(MED):check - d.set_is_oper_up(true); // FIXME(MED):check - d.set_is_exclusive_control(false); // FIXME(MED): check + d.set_is_enabled(true); //! \todo (LOW) admin enable/disable of port + d.set_is_oper_up(true); //! \todo (HIGH) oper up/down of port + d.set_is_exclusive_control(false); //! \todo (HIGH) port exclusive control memset((void*) &stats, 0, sizeof(stats)); resetStats(); @@ -190,7 +165,7 @@ void PortInfo::update() sendQueueList.clear(); returnToQIdx = -1; - // TODO(LOW): calculate sendqueue size + //! \todo (LOW): calculate sendqueue size sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); sendQ.sendQueueCumLen.clear(); @@ -254,7 +229,7 @@ void PortInfo::update() { sendQueueList.append(sendQ); - // TODO(LOW): calculate sendqueue size + //! \todo (LOW): calculate sendqueue size sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); sendQ.sendQueueCumLen.clear(); @@ -291,16 +266,17 @@ void PortInfo::update() goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_id: - // TODO(MED): define and use - // streamList[i].d.control().goto_stream_id(); + /*! \todo (MED): define and use + streamList[i].d.control().goto_stream_id(); */ - // TODO(MED): assumes goto Id is less than current!!!! - // To support goto to any id, do - // if goto_id > curr_id then - // i = goto_id; - // goto restart; - // else - // returnToQIdx = 0; + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ returnToQIdx=0; goto _stop_no_more_pkts; @@ -344,9 +320,9 @@ void PortInfo::stopCapture() capturer.stop(); } -void PortInfo::viewCapture() +QFile* PortInfo::captureFile() { - capturer.view(); + return capturer.captureFile(); } void PortInfo::resetStats() @@ -550,7 +526,7 @@ void PortInfo::PortMonitorRx::callbackRx(u_char *state, // Update RxStats and RxRates using PCAP data usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 + (header->ts.tv_usec - port->lastTsRx.tv_usec); - // TODO(rate) + //! \todo support Rx Pkt/Bit rate on Linux (libpcap callback) #if 0 port->stats.rxPps = (pkts * 1000000) / usec; port->stats.rxBps = (bytes * 1000000) / usec; @@ -581,7 +557,7 @@ void PortInfo::PortMonitorTx::callbackTx(u_char *state, // Update TxStats and TxRates using PCAP data usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + (header->ts.tv_usec - port->lastTsTx.tv_usec); - // TODO(rate) + //! \todo support Tx Pkt/Bit rate on Linux (libpcap callback) #if 0 port->stats.txPps = (pkts * 1000000) / usec; port->stats.txBps = (bytes * 1000000) / usec; @@ -703,7 +679,7 @@ PortInfo::PortTransmitter::PortTransmitter(PortInfo *port) void PortInfo::PortTransmitter::run() { - // TODO(HI): Stream Mode - one pass/continuous + //! \todo (MED) Stream Mode - continuous: define before implement // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 // 'coz of 2 reasons - there's no way of stopping it before all packets @@ -739,14 +715,20 @@ PortInfo::PortCapture::PortCapture(PortInfo *port) dumpHandle = NULL; } +PortInfo::PortCapture::~PortCapture() +{ +} + void PortInfo::PortCapture::run() { + int ret; + if (capHandle == NULL) { char errbuf[PCAP_ERRBUF_SIZE]; - capHandle = pcap_open_live(port->dev->name, 0, - PCAP_OPENFLAG_PROMISCUOUS, 1000 /*ms*/, errbuf); + capHandle = pcap_open_live(port->dev->name, 65535, + PCAP_OPENFLAG_PROMISCUOUS, -1, errbuf); if (capHandle == NULL) { qDebug("Error opening port %s: %s\n", @@ -757,12 +739,27 @@ void PortInfo::PortCapture::run() { if (!capFile.open()) qFatal("Unable to open temp cap file"); - qDebug("cap file = %s", capFile.fileName().toAscii().constData()); } + + qDebug("cap file = %s", capFile.fileName().toAscii().constData()); dumpHandle = pcap_dump_open(capHandle, capFile.fileName().toAscii().constData()); - pcap_loop(capHandle, -1, pcap_dump, (uchar*) dumpHandle); + ret = pcap_loop(capHandle, -1, pcap_dump, (uchar*) dumpHandle); + switch (ret) + { + case -2: + qDebug("%s: breakloop called %d", __PRETTY_FUNCTION__, ret); + break; + + case -1: + case 0: + qFatal("%s: unexpected break from loop (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } } void PortInfo::PortCapture::stop() @@ -776,11 +773,9 @@ void PortInfo::PortCapture::stop() } } -void PortInfo::PortCapture::view() +QFile* PortInfo::PortCapture::captureFile() { - // FIXME: hack - when correcting this remove the include also - QProcess::execute("C:/Program Files/Wireshark/wireshark.exe", - QStringList() << capFile.fileName()); + return &capFile; } @@ -914,7 +909,7 @@ const ::OstProto::PortId* request, { qDebug("%s: Invalid port id %d", __PRETTY_FUNCTION__, portIdx); controller->SetFailed("Invalid Port Id"); - goto _exit; // TODO(LOW): Partial status of RPC + goto _exit; //! \todo (LOW): Partial status of RPC } response->mutable_port_id()->set_id(portIdx); @@ -954,7 +949,7 @@ const ::OstProto::StreamIdList* request, streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); if (streamIndex < 0) - continue; // TODO(LOW): Partial status of RPC + continue; //! \todo(LOW): Partial status of RPC s = response->add_stream(); @@ -989,7 +984,7 @@ const ::OstProto::StreamIdList* request, // If stream with same id as in request exists already ==> error!! streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); if (streamIndex >= 0) - continue; // TODO(LOW): Partial status of RPC + continue; //! \todo (LOW): Partial status of RPC // Append a new "default" stream - actual contents of the new stream is // expected in a subsequent "modifyStream" request - set the stream id @@ -997,7 +992,7 @@ const ::OstProto::StreamIdList* request, s->mStreamId.CopyFrom(request->stream_id(i)); portInfo[portIdx]->streamList.append(s); - // TODO(LOW): fill-in response "Ack"???? + //! \todo (LOW): fill-in response "Ack"???? } portInfo[portIdx]->setDirty(true); _exit: @@ -1027,11 +1022,11 @@ const ::OstProto::StreamIdList* request, streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); if (streamIndex < 0) - continue; // TODO(LOW): Partial status of RPC + continue; //! \todo (LOW): Partial status of RPC delete portInfo[portIdx]->streamList.takeAt(streamIndex); - // TODO(LOW): fill-in response "Ack"???? + //! \todo (LOW): fill-in response "Ack"???? } portInfo[portIdx]->setDirty(true); _exit: @@ -1061,12 +1056,12 @@ const ::OstProto::StreamConfigList* request, streamIndex = getStreamIndex(portIdx, request->stream(i).stream_id().id()); if (streamIndex < 0) - continue; // TODO(LOW): Partial status of RPC + continue; //! \todo (LOW): Partial status of RPC portInfo[portIdx]->streamList[streamIndex]->protoDataCopyFrom( request->stream(i)); - // TODO(LOW): fill-in response "Ack"???? + //! \todo(LOW): fill-in response "Ack"???? } portInfo[portIdx]->setDirty(true); _exit: @@ -1087,7 +1082,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? if (portInfo[portIdx]->isDirty()) portInfo[portIdx]->update(); @@ -1099,12 +1094,12 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? portInfo[portIdx]->startTransmit(); } - // TODO(LOW): fill-in response "Ack"???? + //! \todo (LOW): fill-in response "Ack"???? done->Run(); } @@ -1122,11 +1117,11 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? portInfo[portIdx]->stopTransmit(); } - // TODO(LOW): fill-in response "Ack"???? + //! \todo (LOW): fill-in response "Ack"???? done->Run(); } @@ -1143,7 +1138,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? portInfo[portIdx]->startCapture(); } @@ -1163,7 +1158,7 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? portInfo[portIdx]->stopCapture(); } @@ -1172,25 +1167,25 @@ const ::OstProto::PortIdList* request, } void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::CaptureBufferList* response, +const ::OstProto::PortId* request, +::OstProto::CaptureBuffer* response, ::google::protobuf::Closure* done) { + uint portIdx; qDebug("In %s", __PRETTY_FUNCTION__); - // FIXME: BAD BAD VERY BAD !!!!!! - for (int i=0; i < request->port_id_size(); i++) + portIdx = request->id(); + if (portIdx >= numPorts) { - uint portIdx; - - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? - - portInfo[portIdx]->viewCapture(); + controller->SetFailed("invalid portid"); + goto _exit; } - controller->SetFailed("Not Implemented"); + portInfo[portIdx]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portIdx]->captureFile()); + +_exit: done->Run(); } @@ -1208,7 +1203,7 @@ const ::OstProto::PortIdList* request, portidx = request->port_id(i).id(); if (portidx >= numPorts) - continue; // TODO(LOW): partial rpc? + continue; //! \todo(LOW): partial rpc? s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); @@ -1260,11 +1255,11 @@ const ::OstProto::PortIdList* request, portIdx = request->port_id(i).id(); if (portIdx >= numPorts) - continue; // TODO(LOW): partial RPC? + continue; //! \todo (LOW): partial RPC? portInfo[portIdx]->resetStats(); } - // TODO(LOW): fill-in response "Ack"???? + //! \todo (LOW): fill-in response "Ack"???? done->Run(); } diff --git a/server/myservice.h b/server/myservice.h index 270bfba..fb55283 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -105,9 +105,10 @@ class PortInfo public: PortCapture(PortInfo *port); + ~PortCapture(); void run(); void stop(); - void view(); + QFile* captureFile(); }; OstProto::Port d; @@ -173,7 +174,7 @@ public: void stopTransmit(); void startCapture(); void stopCapture(); - void viewCapture(); + QFile* captureFile(); void resetStats(); }; @@ -241,8 +242,8 @@ public: ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::CaptureBufferList* response, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, ::google::protobuf::Closure* done); virtual void getStats(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 4ad8a51..197e83b 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -79,14 +79,14 @@ _restart: if (*p_stop) return ret; - // TODO(HI): Timing between subsequent sendQueues + //! \todo (HIGH): Timing between subsequent sendQueues } if (returnToQIdx >= 0) { i = returnToQIdx; - // FIXME: 1s fixed; Change this to ipg of last stream + //! \todo (HIGH) 1s fixed; Change this to ipg of last stream (*pf_usleep)(1000000); goto _restart; } diff --git a/server/rxtx.cpp b/server/rxtx.cpp deleted file mode 100644 index 11789f1..0000000 --- a/server/rxtx.cpp +++ /dev/null @@ -1,514 +0,0 @@ - -FIXME(HI): File Not used anymore - -#if 0 -#include "qtglobal" // FIXME: needed only for qdebug -#include "rxtx.h" -#if 0 // PB -#include "../common/protocol.h" -#endif - - - -//#define LOG(...) drone->ui.teLOG->append(QString().sprintf( __VA_ARGS__)) -//#define LOG(...) drone->LOG(QString().sprintf( __VA_ARGS__)) -#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} - - -RxTx::RxTx(AbstractHost *host) -{ - pcap_if_t *d; - int i=0; - char errbuf[PCAP_ERRBUF_SIZE]; - - // Init Data - RxTx::host = host; - numPorts = 0; - alldevs = NULL; - - LOG("Retrieving the device list from the local machine\n"); - if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) - { - LOG("Error in pcap_findalldevs_ex: %s\n", errbuf); - goto _fail; - } - - /* Count number of local ports */ - for(d = alldevs; d != NULL; d = d->next) - numPorts++; - - portInfo = new PortInfo[numPorts]; - - /* Print the list */ - for(i=0, d=alldevs; d!=NULL; i++, d=d->next) - { - 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) - { - LOG(" (%s)\n", d->description); - } - else - LOG(" (No description available)\n"); -#endif - } - - if (i == 0) - { - LOG("\nNo interfaces found! Make sure WinPcap is installed.\n"); - goto _fail; - } - -_fail: - return; -} - -#if 0 -RxTx::LOG(char* fmt, ...) -{ - sprintf(logStr, fmt, _VA_ARGS_); - host->LOG(logStr); -} -#endif - -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 - // but need to fix this as this is a TCP stream - - hdr = (tCommHdr*) msg; - - if (hdr->ver != 1) // FIXME:hardcoding - { - LOG("Rcvd msg with invalid version %d\n", hdr->ver); - goto _exit; - } - - qDebug("msgType - %x: %x\n", hdr->msgType, NTOHS(hdr->msgType)); - switch (NTOHS(hdr->msgType)) - { - case e_MT_GetCapability: - SendCapabilityInfo(); - break; - case e_MT_ChangePortConfig: - ProcessPortConfig(msg+sizeof(tCommHdr), len - sizeof(tCommHdr)); - break; - case e_MT_GetPortConfig: - SendPortInfo(0); // FIXME - break; - - default: - LOG("Rcvd msg with unrecognized msgType %d\n", NTOHS(hdr->msgType)); - } - -_exit: - return; -} - -void RxTx::SendCapabilityInfo(void) -{ - unsigned char *msg, *p; - unsigned int i, msgLen; - - p = msg = (unsigned char*) pktBuff; - ((tCommHdr*)(p))->ver = 1; - ((tCommHdr*)(p))->msgType = HTONS(e_MT_CapabilityInfo); - p += sizeof(tCommHdr); - - for (i = 0; i < numPorts; i++) - { - // TLV: Port Capability - ((tTlvPortCapability*)(p))->tlvType = HTONS(e_TT_PortCapability); - ((tTlvPortCapability*)(p))->tlvLen = HTONS(sizeof(tTlvPortCapability)); - ((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))->portName, "eth"); - //strcat(((tTlvPortCapability*)(p))->name, itoa(portInfo[i].portId, NULL, 10)); - itoa(portInfo[i].portId, &(((tTlvPortCapability*)(p))->portName[3]), 10); -#endif - strncpy(((tTlvPortCapability*)(p))->portDesc, - portInfo[i].dev->description, TLV_MAX_PORT_DESC); - ((tTlvPortCapability*)(p))->portDesc[TLV_MAX_PORT_DESC -1] = 0; - p += sizeof(tTlvPortCapability); - } - msgLen = (p - msg); - ((tCommHdr*)(msg))->msgLen = HTONS(msgLen); - - logStr[0] = 0; - for (i = 0; i < msgLen >> 2; i++) - { - char word[10]; - - sprintf(word, "%08X ", HTONL(((unsigned int *)(msg))[i])); - strcat(logStr, word); - } - host->Log("Sending msg\n"); - host->Log(logStr); -#if 0 // PB - host->SendMsg(pktBuff, msgLen); -#endif -} - -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) -{ - // FIXME -} - -/* -** --------------------- 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; -} -#endif diff --git a/server/rxtx.h b/server/rxtx.h deleted file mode 100644 index 7b86991..0000000 --- a/server/rxtx.h +++ /dev/null @@ -1,84 +0,0 @@ - -FIXME(HI): File not used anymore - -#if 0 - -#ifndef _RXTX_H -#define _RXTX_H - -#include "pcap.h" -#include "abstracthost.h" - -#include "../common/protocol.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 -{ - public: - RxTx(AbstractHost* host); - ~RxTx(); - void ProcessMsg(const char* msg, int len); - - private: - AbstractHost *host; - char logStr[1024]; - -#define MAX_PKT_SIZE 1024 - unsigned char pktBuff[MAX_PKT_SIZE]; - unsigned numPorts; - 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 -#endif From ade8c119d9d171f79bcefe798a0cf411d7de4962 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 8 Nov 2009 08:20:34 +0000 Subject: [PATCH 028/294] Features - Port State (Link/Transmit/Capture) now updated alongwith port stats - On link state change, the port window is not updated - partial changes have been done under #if 0; needs refactoring of Port Class implementation/usage before a signal/slot for the same can be implemented Fixes - Fixed crash in client when connection to server is broken - Packet Capture and Capture Buffer Retrieval now works correctly and consistently (I think!) Others - Minor visual changes in Ports Window - Port Stats Window now has 'right' alignment for stats data and 'center' for state data --- client/icons/bullet_white.png | Bin 0 -> 201 bytes client/ostinato.qrc | 1 + client/port.cpp | 7 ++ client/port.h | 11 +++- client/portgroup.h | 2 +- client/portgrouplist.cpp | 4 +- client/portmodel.cpp | 18 +++++- client/portmodel.h | 2 +- client/portstatsmodel.cpp | 26 +++++++- client/portstatsmodel.h | 32 ++++++++- client/portswindow.cpp | 9 ++- client/portswindow.h | 1 + client/portswindow.ui | 43 ++++++++++++- common/protocol.proto | 16 ++++- server/myservice.cpp | 118 +++++++++++++++++++++++++--------- server/myservice.h | 13 ++++ 16 files changed, 257 insertions(+), 46 deletions(-) create mode 100644 client/icons/bullet_white.png diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 48ab66a..1b09036 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -7,6 +7,7 @@ icons/bullet_green.png icons/bullet_orange.png icons/bullet_red.png + icons/bullet_white.png icons/bullet_yellow.png icons/control_play.png icons/control_stop.png diff --git a/client/port.cpp b/client/port.cpp index 291b1b5..d7e2b4c 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -183,6 +183,13 @@ void Port::when_syncComplete() void Port::updateStats(OstProto::PortStats *portStats) { + OstProto::PortState oldState; + + oldState = stats.state(); stats.MergeFrom(*portStats); +#if 0 + if (oldState.link_state() != stats.state().link_state()) + emit portDataChanged(mPortGroupId, mPortId); +#endif } diff --git a/client/port.h b/client/port.h index 126b28d..bdafdb9 100644 --- a/client/port.h +++ b/client/port.h @@ -30,7 +30,6 @@ private: void reorderStreamsByOrdinals(); public: enum AdminStatus { AdminDisable, AdminEnable }; - enum OperStatus { OperDown, OperUp }; enum ControlMode { ControlShared, ControlExclusive }; // FIXME(HIGH): default args is a hack for QList operations on Port @@ -48,8 +47,6 @@ public: { return QString().fromStdString(d.description()); } AdminStatus adminStatus() { return (d.is_enabled()?AdminEnable:AdminDisable); } - OperStatus operStatus() - { return (d.is_oper_up()?OperUp:OperDown); } ControlMode controlMode() { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } @@ -70,6 +67,9 @@ public: Q_ASSERT(index < mStreams.size()); return mStreams[index]; } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + OstProto::PortStats getStats() { return stats; } // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal @@ -97,6 +97,11 @@ public: void updateStats(OstProto::PortStats *portStats); +#if 0 +signals: + void portDataChanged(int portGroupId, int portId); +#endif + }; #endif diff --git a/client/portgroup.h b/client/portgroup.h index c74640a..81363cc 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -91,7 +91,7 @@ public: void processClearStatsAck(OstProto::Ack *ack); signals: - void portGroupDataChanged(PortGroup* portGroup); + void portGroupDataChanged(PortGroup* portGroup, int portId = 0xFFFF); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); void statsChanged(quint32 portGroupId); diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index a503449..4387aee 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -60,8 +60,8 @@ void PortGroupList::addPortGroup(PortGroup &portGroup) { mPortGroupListModel.portGroupAboutToBeAppended(); - connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*)), - &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*))); + connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*, int))); #if 0 connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 61f9be8..9397cc1 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -144,7 +144,17 @@ QVariant PortModel::data(const QModelIndex &index, int role) const DBG0("Exit PortModel data 5\n"); if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) return QVariant(); - return QIcon(":/icons/bullet_green.png"); + switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()].linkState()) + { + case OstProto::LinkStateUnknown: + return QIcon(":/icons/bullet_white.png"); + case OstProto::LinkStateDown: + return QIcon(":/icons/bullet_red.png"); + case OstProto::LinkStateUp: + return QIcon(":/icons/bullet_green.png"); + default: + qFatal("unexpected/unimplemented port oper state"); + } } else { @@ -152,6 +162,8 @@ QVariant PortModel::data(const QModelIndex &index, int role) const return QVariant(); } } + + return QVariant(); } QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -252,7 +264,7 @@ quint32 PortModel::portId(const QModelIndex& index) // ---------------------------------------------- // Slots // ---------------------------------------------- -void PortModel::when_portGroupDataChanged(PortGroup* portGroup) +void PortModel::when_portGroupDataChanged(PortGroup* portGroup, int portId) { QModelIndex index; @@ -266,7 +278,7 @@ void PortModel::when_portGroupDataChanged(PortGroup* portGroup) qDebug("when_portGroupDataChanged idx = %d", pgl->mPortGroups.indexOf(portGroup)); index = createIndex(pgl->mPortGroups.indexOf(portGroup), 0, - (portGroup->id() << 16) | 0xFFFF); + (portGroup->id() << 16) | portId); emit dataChanged(index, index); } diff --git a/client/portmodel.h b/client/portmodel.h index b68acdc..de2b140 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -32,7 +32,7 @@ class PortModel : public QAbstractItemModel private slots: - void when_portGroupDataChanged(PortGroup *portGroup); + void when_portGroupDataChanged(PortGroup *portGroup, int portId); void portGroupAboutToBeAppended(); void portGroupAppended(); diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 9961e12..6d77945 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -67,13 +67,15 @@ void PortStatsModel::getDomainIndexes(const QModelIndex &index, QVariant PortStatsModel::data(const QModelIndex &index, int role) const { uint pgidx, pidx; + int row; // Check for a valid index if (!index.isValid()) return QVariant(); // Check for row/column limits - if (index.row() >= e_STAT_MAX) + row = index.row(); + if (row >= e_STAT_MAX) return QVariant(); if (numPorts.isEmpty()) @@ -91,8 +93,19 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx].getStats(); - switch(index.row()) + switch(row) { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics case e_STAT_FRAMES_RCVD: return stats.rx_pkts(); @@ -136,6 +149,15 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const return 0; } } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } else return QVariant(); diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index de1c585..02f02dd 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -5,7 +5,19 @@ #include typedef enum { - e_STAT_FRAMES_RCVD = 0, + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, e_STAT_FRAMES_SENT, e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_RECV_RATE, @@ -19,10 +31,17 @@ typedef enum { e_STAT_BYTES_RCVD_NIC, e_STAT_BYTES_SENT_NIC, #endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + e_STAT_MAX } PortStat; static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + << "Frames Received" << "Frames Sent" << "Frame Send Rate (fps)" @@ -39,6 +58,17 @@ static QStringList PortStatName = (QStringList() #endif ); +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + class PortGroupList; class PortStatsModel : public QAbstractTableModel diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 8b75b65..7d9bbc4 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -36,6 +36,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) this, SLOT(when_portModel_dataChanged(const QModelIndex&, const QModelIndex&))); + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portView_currentChanged(const QModelIndex&, @@ -139,7 +142,11 @@ void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, { updatePortViewActions(tvPortList->currentIndex()); } - +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } #if 0 diff --git a/client/portswindow.h b/client/portswindow.h index a5bc562..b291f91 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -40,6 +40,7 @@ private slots: void when_streamView_selectionChanged(); void when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); + void when_portModel_reset(); void on_pbApply_clicked(); diff --git a/client/portswindow.ui b/client/portswindow.ui index b58c356..1ed9485 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -6,7 +6,7 @@ 0 0 689 - 389 + 254 @@ -28,10 +28,28 @@ - 1 + 0 + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 6 + @@ -118,11 +136,32 @@ Control + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + Qt::ActionsContextMenu + + QFrame::NoFrame + + + 0 + QAbstractItemView::ExtendedSelection diff --git a/common/protocol.proto b/common/protocol.proto index 1f3c0a5..6557dbb 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -128,7 +128,6 @@ message Port { optional string name = 2; optional string description = 3; optional bool is_enabled = 4; - optional bool is_oper_up = 5; optional bool is_exclusive_control = 6; } @@ -149,9 +148,24 @@ message CaptureBufferList { repeated CaptureBuffer list = 1; } +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + message PortStats { + required PortId port_id = 1; + optional PortState state = 2; + optional uint64 rx_pkts = 11; optional uint64 rx_bytes = 12; optional uint64 rx_pkts_nic = 13; diff --git a/server/myservice.cpp b/server/myservice.cpp index cb9a2db..54b0895 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -68,6 +68,20 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) this->dev = dev; +#ifdef Q_OS_WIN32 + adapter = PacketOpenAdapter(dev->name); + if (!adapter) + qFatal("Unable to open adapter %s", dev->name); + oidData = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + sizeof(uint)); + if (oidData) + { + memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + oidData->Length=sizeof(uint); + } + else + qFatal("failed to alloc oidData"); +#endif + /* * Get 2 device handles - one for rx and one for tx. If we use only * one handle for both rx and tx anythin that we tx using the single @@ -131,12 +145,13 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) if (dev->description) d.set_description(dev->description); d.set_is_enabled(true); //! \todo (LOW) admin enable/disable of port - d.set_is_oper_up(true); //! \todo (HIGH) oper up/down of port d.set_is_exclusive_control(false); //! \todo (HIGH) port exclusive control memset((void*) &stats, 0, sizeof(stats)); resetStats(); + linkState = OstProto::LinkStateUnknown; + // We'll create sendqueue later when required sendQueueList.clear(); returnToQIdx = -1; @@ -149,6 +164,35 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev) monitorTx.start(); } +void PortInfo::updateLinkState() +{ +#ifdef Q_OS_WIN32 + OstProto::LinkState newLinkState + = OstProto::LinkStateUnknown; + + memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + oidData->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + oidData->Length = sizeof(uint); + if (PacketRequest(adapter, 0, oidData)) + { + uint state; + + if (oidData->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)oidData->Data, oidData->Length); + if (state == 0) + newLinkState = OstProto::LinkStateUp; + else if (state == 1) + newLinkState = OstProto::LinkStateDown; + } + } + + linkState = newLinkState; +#elif defined(Q_OS_LINUX) + //! \todo (HI) implement link state for linux - get from /proc maybe? +#endif +} + void PortInfo::update() { uchar pktBuf[2000]; @@ -211,6 +255,9 @@ void PortInfo::update() { int len; + /*! \todo (HIGH) if pkt contents do not change across + pkts then don't call makePacket(), rather reuse the + previous */ len = streamList[i]->makePacket(pktBuf, sizeof(pktBuf), j * numPackets + k); if (len > 0) @@ -722,55 +769,60 @@ PortInfo::PortCapture::~PortCapture() void PortInfo::PortCapture::run() { int ret; + char errbuf[PCAP_ERRBUF_SIZE]; + capHandle = pcap_open_live(port->dev->name, 65535, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); if (capHandle == NULL) { - char errbuf[PCAP_ERRBUF_SIZE]; - - capHandle = pcap_open_live(port->dev->name, 65535, - PCAP_OPENFLAG_PROMISCUOUS, -1, errbuf); - if (capHandle == NULL) - { - qDebug("Error opening port %s: %s\n", - port->dev->name, pcap_geterr(capHandle)); - } + qDebug("Error opening port %s: %s\n", + port->dev->name, pcap_geterr(capHandle)); + return; } + if (!capFile.isOpen()) { if (!capFile.open()) qFatal("Unable to open temp cap file"); + return; } qDebug("cap file = %s", capFile.fileName().toAscii().constData()); dumpHandle = pcap_dump_open(capHandle, capFile.fileName().toAscii().constData()); - ret = pcap_loop(capHandle, -1, pcap_dump, (uchar*) dumpHandle); - switch (ret) + m_stop = 0; + while (m_stop == 0) { - case -2: - qDebug("%s: breakloop called %d", __PRETTY_FUNCTION__, ret); - break; + struct pcap_pkthdr *hdr; + const uchar *data; - case -1: - case 0: - qFatal("%s: unexpected break from loop (%d): %s", - __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); - break; - default: - qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + ret = pcap_next_ex(capHandle, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle, hdr, data); + case 0: + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } } + m_stop = 0; + pcap_dump_close(dumpHandle); + pcap_close(capHandle); + dumpHandle = NULL; + capHandle = NULL; } void PortInfo::PortCapture::stop() { - pcap_breakloop(capHandle); - if (dumpHandle) - { - pcap_dump_flush(dumpHandle); - pcap_dump_close(dumpHandle); - dumpHandle = NULL; - } + m_stop = 1; } QFile* PortInfo::PortCapture::captureFile() @@ -1200,6 +1252,7 @@ const ::OstProto::PortIdList* request, { uint portidx; ::OstProto::PortStats *s; + OstProto::PortState *st; portidx = request->port_id(i).id(); if (portidx >= numPorts) @@ -1216,6 +1269,13 @@ const ::OstProto::PortIdList* request, } #endif + portInfo[portidx]->updateLinkState(); + + st = s->mutable_state(); + st->set_link_state(portInfo[portidx]->linkState); + st->set_is_transmit_on(portInfo[portidx]->transmitter.isRunning()); + st->set_is_capture_on(portInfo[portidx]->capturer.isRunning()); + s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - portInfo[portidx]->epochStats.rxPkts); s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - diff --git a/server/myservice.h b/server/myservice.h index fb55283..ebd1db1 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -22,6 +22,10 @@ #ifdef Q_OS_WIN32 #include #endif + +#ifdef Q_OS_WIN32 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#endif #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 @@ -29,6 +33,7 @@ //! 7 byte Preamble + 1 byte SFD + 4 byte FCS #define ETH_FRAME_HDR_SIZE 12 + class MyService; class StreamInfo : public StreamBase @@ -99,6 +104,7 @@ class PortInfo friend class PortInfo; PortInfo *port; + int m_stop; pcap_t *capHandle; pcap_dumper_t *dumpHandle; QTemporaryFile capFile; @@ -110,8 +116,14 @@ class PortInfo void stop(); QFile* captureFile(); }; + +#ifdef Q_OS_WIN32 + LPADAPTER adapter; + PPACKET_OID_DATA oidData; +#endif OstProto::Port d; + OstProto::LinkState linkState; struct PortStats { @@ -167,6 +179,7 @@ class PortInfo public: PortInfo(uint id, pcap_if_t *dev); uint id() { return d.port_id().id(); } + void updateLinkState(); bool isDirty() { return isSendQueueDirty; } void setDirty(bool dirty) { isSendQueueDirty = dirty; } void update(); From 17792b825357982fa7477aab9033dc1b2b1b80ea Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 13 Nov 2009 16:00:57 +0000 Subject: [PATCH 029/294] Refactoring, optimization et. al. --------------------------------- - StreamConfigDialog: Valid subsequent protocol choices for a particular protocol in the simple protocol selection widget is no longer hardcoded - ProtocolManager is queried for validitity of each pair of possible protocols; signal-slot connections are made accordingly. This refactoring makes it easier to add a protocol to the simple protocol selection widget - ProtocolManager: populates and maintains a database of valid 'neighbour protocols' and implements a method - isValidNeighbour() to query the same for a pair of protocols - AbstractProtocol: new method protocolIdType() introduced to build the above database (in conjunction with the existing method protocolId(ProtocolIdType)); default implementation returns ProtocolIdNone - Protocols which include a valid/supported ProtocolIdType (eth/llc/ip) reimplement protocolIdType() to return the apporpirate ProtocolIdType. These are viz. - combo - eth - llc - snap - ip - Speed optimization while populating streamqueues if the protocolFrameValue/Size() does not vary across packets - AbstractProtocol: new methods to support the above optimization - isProtocolFrameValueVariable() - isProtocolFrameSizeVariable() - isProtocolFramePayloadValueVariable() - isProtocolFramePayloadSizeVariable() (each of the default implementations returns false indicating that the protocol frame value or frame size is fixed and not variable) - Protocols which support variable values/size (list follows) reimplement the above methods appropriately - combo - mac - dot3 - ip4 - tcp - udp - payload - StreamInfo::makePacket() moved to base class as StreamBase::frameValue() - StreamBase: all 'get' accessor functions made 'const' - class ProtocolManager: while registering a protocol, no need to pass the protocol name; ProtocolManager finds it out internally by using the protocol's shortName() method Fixes ----- - Fixed issue with port capture not starting the first time 'start capture' was clicked --- client/streamconfigdialog.cpp | 104 ++++++++++++++++++---------------- common/abstractprotocol.cpp | 50 +++++++++++++++- common/abstractprotocol.h | 7 +++ common/comboprotocol.h | 19 +++++++ common/dot3.cpp | 4 ++ common/dot3.h | 2 + common/eth2.cpp | 5 ++ common/eth2.h | 2 + common/ip4.cpp | 14 +++++ common/ip4.h | 3 + common/llc.cpp | 5 ++ common/llc.h | 2 + common/mac.cpp | 9 +++ common/mac.h | 2 + common/payload.cpp | 17 ++++++ common/payload.h | 3 + common/protocolmanager.cpp | 69 ++++++++++++++++------ common/protocolmanager.h | 9 ++- common/snap.cpp | 5 ++ common/snap.h | 2 + common/streambase.cpp | 87 +++++++++++++++++++++++----- common/streambase.h | 29 +++++----- common/tcp.cpp | 7 +++ common/tcp.h | 2 + common/udp.cpp | 7 +++ common/udp.h | 2 + server/myservice.cpp | 56 ++++++------------ server/myservice.h | 3 - 28 files changed, 387 insertions(+), 139 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 30649b3..5201099 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -43,60 +43,60 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // Time to play match the signals and slots! - // If L1/FT = None, force subsequent protocol level(s) also to None - connect(rbL1None, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - // Enable/Disable L3 Protocol Choices for FT Ethernet2 - connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); - connect(rbFtEthernet2, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); - - // Force L3 = None if FT = 802.3 Raw - connect(rbFt802Dot3Raw, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setDisabled(bool))); - connect(rbFt802Dot3Raw, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); - - // Force L3 = None if FT = 802.3 LLC (to ensure a valid L3 is selected) - connect(rbFt802Dot3Llc, SIGNAL(clicked(bool)), rbL3None, SLOT(click())); - - // Enable/Disable L3 Protocol Choices for FT 802Dot3Llc - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); - connect(rbFt802Dot3Llc, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setDisabled(bool))); - - // Enable/Disable L3 Protocol Choices for FT 802.3 LLC SNAP - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Ipv4, SLOT(setEnabled(bool))); - connect(rbFtLlcSnap, SIGNAL(toggled(bool)), rbL3Arp, SLOT(setEnabled(bool))); - - // Force L3 = Other if FT = Other + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); - - // If L3 = None, force subsequent protocol level also to None - connect(rbL3None, SIGNAL(toggled(bool)), this, SLOT(forceProtocolNone(bool))); - - // Enable/Disable L4 Protocol Choices for L3 Protocol IPv4 - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setEnabled(bool))); - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setEnabled(bool))); - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setEnabled(bool))); - connect(rbL3Ipv4, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setEnabled(bool))); - - // Enable/Disable L4 Protocol Choices for L3 Protocol ARP - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Icmp, SLOT(setDisabled(bool))); - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Igmp, SLOT(setDisabled(bool))); - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Tcp, SLOT(setDisabled(bool))); - connect(rbL3Arp, SIGNAL(toggled(bool)), rbL4Udp, SLOT(setDisabled(bool))); - - // Force L4 Protocol = None if L3 Protocol is set to ARP - connect(rbL3Arp, SIGNAL(clicked(bool)), rbL4None, SLOT(click())); - - // Force L4 = Other if L3 = Other connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); - - // Force Payload = Other if L4 = Other connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + // Setup valid subsequent protocols for L2 and L3 protocols + for (int i = ProtoL2; i <= ProtoL3; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager.isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + } + } + } + mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager.protocolDatabase(), this); lvAllProtocols->setModel(mpAvailableProtocolsModel); @@ -729,8 +729,16 @@ void StreamConfigDialog::updateSelectProtocolsSimpleWidget() id = _iter->next()->protocolNumber(); btn = bgProto[i]->button(id); - if (btn && btn->isEnabled()) - btn->click(); + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } else { switch (i) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 10678fb..3dd58ae 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -44,7 +44,7 @@ AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream, quint32 AbstractProtocol::protocolNumber() const { - qDebug("Something wrong!!!"); + qFatal("Something wrong!!!"); return 0xFFFFFFFF; } @@ -193,6 +193,11 @@ bool AbstractProtocol::setFieldData(int index, const QVariant &value, return false; } +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + quint32 AbstractProtocol::protocolId(ProtocolIdType type) const { return 0; @@ -360,6 +365,49 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) return proto; } +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + + /*! \note If a subclass uses protocolFrameCksum() from within fieldData() to derive a cksum field, it MUST handle and return the 'FieldBitSize' diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 3e1dc12..ccf1b12 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -56,6 +56,7 @@ public: }; enum ProtocolIdType { + ProtocolIdNone, ProtocolIdLlc, ProtocolIdEth, ProtocolIdIp, @@ -82,6 +83,7 @@ public: virtual QString name() const; virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; quint32 payloadProtocolId(ProtocolIdType type) const; @@ -101,6 +103,11 @@ public: int protocolFrameOffset(int streamIndex = 0) const; int protocolFramePayloadSize(int streamIndex = 0) const; + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, diff --git a/common/comboprotocol.h b/common/comboprotocol.h index 34cba86..dbfd682 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -84,6 +84,11 @@ public: return protoA->shortName() + "/" + protoB->shortName(); } + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + virtual quint32 protocolId(ProtocolIdType type) const { return protoA->protocolId(type); @@ -133,7 +138,21 @@ public: virtual int protocolFrameSize() const; int protocolFrameOffset() const; int protocolFramePayloadSize() const; +#endif + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + +#if 0 virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, diff --git a/common/dot3.cpp b/common/dot3.cpp index 0a88d50..ac2a878 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -119,6 +119,10 @@ bool Dot3Protocol::setFieldData(int index, const QVariant &value, return false; } +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} QWidget* Dot3Protocol::configWidget() { diff --git a/common/dot3.h b/common/dot3.h index def1031..9e0e8c1 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -46,6 +46,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual bool isProtocolFrameValueVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/eth2.cpp b/common/eth2.cpp index 7a6f7b5..0028b51 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -54,6 +54,11 @@ QString Eth2Protocol::shortName() const return QString("Eth II"); } +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + int Eth2Protocol::fieldCount() const { return eth2_fieldCount; diff --git a/common/eth2.h b/common/eth2.h index 0555d65..fcf6b7d 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -39,6 +39,8 @@ public: virtual QString name() const; virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual int fieldCount() const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/ip4.cpp b/common/ip4.cpp index c929872..4c28bfb 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -94,6 +94,11 @@ QString Ip4Protocol::shortName() const return QString("IPv4"); } +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + quint32 Ip4Protocol::protocolId(ProtocolIdType type) const { switch(type) @@ -572,6 +577,15 @@ bool Ip4Protocol::setFieldData(int index, const QVariant &value, return isOk; } +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { diff --git a/common/ip4.h b/common/ip4.h index e378755..34f4c37 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -71,6 +71,7 @@ public: virtual QString name() const; virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; @@ -82,6 +83,8 @@ public: virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; + virtual bool isProtocolFrameValueVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/llc.cpp b/common/llc.cpp index e949e08..25cc307 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -54,6 +54,11 @@ QString LlcProtocol::shortName() const return QString("LLC"); } +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + int LlcProtocol::fieldCount() const { return llc_fieldCount; diff --git a/common/llc.h b/common/llc.h index 3555e92..741f493 100644 --- a/common/llc.h +++ b/common/llc.h @@ -44,6 +44,8 @@ public: virtual QString name() const; virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual int fieldCount() const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/mac.cpp b/common/mac.cpp index 8e468a3..1d7ed87 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -239,6 +239,15 @@ bool MacProtocol::setFieldData(int index, const QVariant &value, return false; } +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + QWidget* MacProtocol::configWidget() { diff --git a/common/mac.h b/common/mac.h index a493bd6..b6165b7 100644 --- a/common/mac.h +++ b/common/mac.h @@ -61,6 +61,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual bool isProtocolFrameValueVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/payload.cpp b/common/payload.cpp index 1125b73..4bbca2e 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -187,6 +187,23 @@ bool PayloadProtocol::setFieldData(int index, const QVariant &value, return false; } +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + QWidget* PayloadProtocol::configWidget() { diff --git a/common/payload.h b/common/payload.h index c2d5f4d..9437a8c 100644 --- a/common/payload.h +++ b/common/payload.h @@ -54,6 +54,9 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 56c1912..8a9547d 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -24,42 +24,69 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ registerProtocol(OstProto::Protocol::kMacFieldNumber, - QString("mac"), (void*) MacProtocol::createInstance); + (void*) MacProtocol::createInstance); registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - QString("payload"), (void*) PayloadProtocol::createInstance); + (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, - QString("eth2"), (void*) Eth2Protocol::createInstance); + (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kDot3FieldNumber, - QString("dot3"), (void*) Dot3Protocol::createInstance); + (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, - QString("llc"), (void*) LlcProtocol::createInstance); + (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, - QString("snap"), (void*) SnapProtocol::createInstance); + (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, - QString("dot2Llc"), (void*) Dot2LlcProtocol::createInstance); + (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, - QString("dot2Snap"), (void*) Dot2SnapProtocol::createInstance); + (void*) Dot2SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kSvlanFieldNumber, - QString("svlan"), (void*) VlanProtocol::createInstance); + (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, - QString("vlan"), (void*) VlanProtocol::createInstance); + (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, - QString("vlanstack"), (void*) VlanStackProtocol::createInstance); + (void*) VlanStackProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, - QString("ip4"), (void*) Ip4Protocol::createInstance); + (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, - QString("tcp"), (void*) TcpProtocol::createInstance); + (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, - QString("udp"), (void*) UdpProtocol::createInstance); + (void*) UdpProtocol::createInstance); + + populateNeighbourProtocols(); } -void ProtocolManager::registerProtocol(int protoNumber, QString protoName, +void ProtocolManager::registerProtocol(int protoNumber, void *protoInstanceCreator) { + AbstractProtocol *p; + //! \todo (MED) validate incoming params for duplicates with existing - nameToNumberMap.insert(protoName, protoNumber); - numberToNameMap.insert(protoNumber, protoName); + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.append(p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } } AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, @@ -84,6 +111,14 @@ AbstractProtocol* ProtocolManager::createProtocol(QString protoName, return createProtocol(nameToNumberMap.value(protoName), stream, parent); } +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + QStringList ProtocolManager::protocolDatabase() { return numberToNameMap.values(); diff --git a/common/protocolmanager.h b/common/protocolmanager.h index 985253b..c619099 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -11,19 +11,24 @@ class ProtocolManager { QMap numberToNameMap; QMap nameToNumberMap; + QMultiMap neighbourProtocols; QMap factory; + QList protocolList; + + void populateNeighbourProtocols(); public: ProtocolManager(); - void registerProtocol(int protoNumber, QString protoName, - void *protoCreator); + void registerProtocol(int protoNumber, void *protoInstanceCreator); AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, AbstractProtocol *parent = 0); AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, AbstractProtocol *parent = 0); + bool isValidNeighbour(int protoPrefix, int protoSuffix); + QStringList protocolDatabase(); }; diff --git a/common/snap.cpp b/common/snap.cpp index 3dfd7e4..0f7d477 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -54,6 +54,11 @@ QString SnapProtocol::shortName() const return QString("SNAP"); } +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + quint32 SnapProtocol::protocolId(ProtocolIdType type) const { switch(type) diff --git a/common/snap.h b/common/snap.h index a2531f6..c4018ad 100644 --- a/common/snap.h +++ b/common/snap.h @@ -39,6 +39,8 @@ public: virtual QString name() const; virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; diff --git a/common/streambase.cpp b/common/streambase.cpp index 1ed8d7f..5bd2dcb 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -20,11 +20,13 @@ StreamBase::StreamBase() : iter = createProtocolListIterator(); // By default newly created streams have the mac and payload protocols - proto = OstProtocolManager.createProtocol("mac", this); + proto = OstProtocolManager.createProtocol( + OstProto::Protocol::kMacFieldNumber, this); iter->insert(proto); qDebug("stream: mac = %p", proto); - proto = OstProtocolManager.createProtocol("payload", this); + proto = OstProtocolManager.createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); iter->insert(proto); qDebug("stream: payload = %p", proto); @@ -104,7 +106,7 @@ void StreamBase::setFrameProtocol(ProtocolList protocolList) } #endif -ProtocolListIterator* StreamBase::createProtocolListIterator() +ProtocolListIterator* StreamBase::createProtocolListIterator() const { return new ProtocolListIterator(*currentFrameProtocols); } @@ -158,7 +160,7 @@ bool StreamBase::setName(QString name) return true; } -StreamBase::FrameLengthMode StreamBase::lenMode() +StreamBase::FrameLengthMode StreamBase::lenMode() const { return (StreamBase::FrameLengthMode) mCore->len_mode(); } @@ -169,7 +171,7 @@ bool StreamBase::setLenMode(FrameLengthMode lenMode) return true; } -quint16 StreamBase::frameLen(int streamIndex) +quint16 StreamBase::frameLen(int streamIndex) const { int pktLen; @@ -211,7 +213,7 @@ bool StreamBase::setFrameLen(quint16 frameLen) return true; } -quint16 StreamBase::frameLenMin() +quint16 StreamBase::frameLenMin() const { return mCore->frame_len_min(); } @@ -222,7 +224,7 @@ bool StreamBase::setFrameLenMin(quint16 frameLenMin) return true; } -quint16 StreamBase::frameLenMax() +quint16 StreamBase::frameLenMax() const { return mCore->frame_len_max(); } @@ -233,17 +235,18 @@ bool StreamBase::setFrameLenMax(quint16 frameLenMax) return true; } -StreamBase::SendUnit StreamBase::sendUnit() +StreamBase::SendUnit StreamBase::sendUnit() const { return (StreamBase::SendUnit) mControl->unit(); } + bool StreamBase::setSendUnit(SendUnit sendUnit) { mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); return true; } -StreamBase::SendMode StreamBase::sendMode() +StreamBase::SendMode StreamBase::sendMode() const { return (StreamBase::SendMode) mControl->mode(); } @@ -255,7 +258,7 @@ bool StreamBase::setSendMode(SendMode sendMode) return true; } -StreamBase::NextWhat StreamBase::nextWhat() +StreamBase::NextWhat StreamBase::nextWhat() const { return (StreamBase::NextWhat) mControl->next(); } @@ -266,7 +269,7 @@ bool StreamBase::setNextWhat(NextWhat nextWhat) return true; } -quint32 StreamBase::numPackets() +quint32 StreamBase::numPackets() const { return (quint32) mControl->num_packets(); } @@ -277,7 +280,7 @@ bool StreamBase::setNumPackets(quint32 numPackets) return true; } -quint32 StreamBase::numBursts() +quint32 StreamBase::numBursts() const { return (quint32) mControl->num_bursts(); } @@ -288,7 +291,7 @@ bool StreamBase::setNumBursts(quint32 numBursts) return true; } -quint32 StreamBase::burstSize() +quint32 StreamBase::burstSize() const { return (quint32) mControl->packets_per_burst(); } @@ -299,7 +302,7 @@ bool StreamBase::setBurstSize(quint32 packetsPerBurst) return true; } -quint32 StreamBase::packetRate() +quint32 StreamBase::packetRate() const { return (quint32) mControl->packets_per_sec(); } @@ -310,7 +313,7 @@ bool StreamBase::setPacketRate(quint32 packetsPerSec) return true; } -quint32 StreamBase::burstRate() +quint32 StreamBase::burstRate() const { return (quint32) mControl->bursts_per_sec(); } @@ -320,3 +323,57 @@ bool StreamBase::setBurstRate(quint32 burstsPerSec) mControl->set_bursts_per_sec(burstsPerSec); return true; } + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const +{ + int pktLen, len = 0; + + pktLen = frameLen(n); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= 4; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(n); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + return pktLen; +} + diff --git a/common/streambase.h b/common/streambase.h index 878b2d7..45f0cbb 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -26,7 +26,7 @@ public: void protoDataCopyFrom(const OstProto::Stream &stream); void protoDataCopyInto(OstProto::Stream &stream) const; - ProtocolListIterator* createProtocolListIterator(); + ProtocolListIterator* createProtocolListIterator() const; //! \todo (LOW) should we have a copy constructor?? @@ -76,41 +76,44 @@ public: bool setName(QString name) ; // Frame Length (includes FCS); - FrameLengthMode lenMode(); + FrameLengthMode lenMode() const; bool setLenMode(FrameLengthMode lenMode); - quint16 frameLen(int streamIndex = 0); + quint16 frameLen(int streamIndex = 0) const; bool setFrameLen(quint16 frameLen); - quint16 frameLenMin(); + quint16 frameLenMin() const; bool setFrameLenMin(quint16 frameLenMin); - quint16 frameLenMax(); + quint16 frameLenMax() const; bool setFrameLenMax(quint16 frameLenMax); - SendUnit sendUnit(); + SendUnit sendUnit() const; bool setSendUnit(SendUnit sendUnit); - SendMode sendMode(); + SendMode sendMode() const; bool setSendMode(SendMode sendMode); - NextWhat nextWhat(); + NextWhat nextWhat() const; bool setNextWhat(NextWhat nextWhat); - quint32 numPackets(); + quint32 numPackets() const; bool setNumPackets(quint32 numPackets); - quint32 numBursts(); + quint32 numBursts() const; bool setNumBursts(quint32 numBursts); - quint32 burstSize(); + quint32 burstSize() const; bool setBurstSize(quint32 packetsPerBurst); - quint32 packetRate(); + quint32 packetRate() const; bool setPacketRate(quint32 packetsPerSec); - quint32 burstRate(); + quint32 burstRate() const; bool setBurstRate(quint32 burstsPerSec); + + bool isFrameVariable() const; + int frameValue(uchar *buf, int bufMaxSize, int n) const; }; #endif diff --git a/common/tcp.cpp b/common/tcp.cpp index be16120..6bbc271 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -380,6 +380,13 @@ bool TcpProtocol::setFieldData(int index, const QVariant &value, return false; } +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} QWidget* TcpProtocol::configWidget() { diff --git a/common/tcp.h b/common/tcp.h index cfd43a6..72cfbf8 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -67,6 +67,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual bool isProtocolFrameValueVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/udp.cpp b/common/udp.cpp index 0d5b772..9cb74ec 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -258,6 +258,13 @@ bool UdpProtocol::setFieldData(int index, const QVariant &value, return false; } +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} QWidget* UdpProtocol::configWidget() { diff --git a/common/udp.h b/common/udp.h index 278809a..663456b 100644 --- a/common/udp.h +++ b/common/udp.h @@ -54,6 +54,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual bool isProtocolFrameValueVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/server/myservice.cpp b/server/myservice.cpp index 54b0895..93afc8b 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -25,39 +25,6 @@ StreamInfo::~StreamInfo() { } -int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n) -{ - int pktLen, len = 0; - - pktLen = frameLen(n); - - // pktLen is adjusted for CRC/FCS which will be added by the NIC - pktLen -= 4; - - if ((pktLen < 0) || (pktLen > bufMaxSize)) - return 0; - - ProtocolListIterator *iter; - - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol *proto; - QByteArray ba; - - proto = iter->next(); - ba = proto->protocolFrameValue(n); - - if (len + ba.size() < bufMaxSize) - memcpy(buf+len, ba.constData(), ba.size()); - len += ba.size(); - } - delete iter; - - return pktLen; -} - - // // ------------------ PortInfo -------------------- // @@ -195,6 +162,8 @@ void PortInfo::updateLinkState() void PortInfo::update() { + int len; + bool isVariable; uchar pktBuf[2000]; pcap_pkthdr pktHdr; ost_pcap_send_queue sendQ; @@ -249,17 +218,25 @@ void PortInfo::update() numBursts, numPackets); qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + if (streamList[i]->isFrameVariable()) + { + isVariable = true; + } + else + { + isVariable = false; + len = streamList[i]->frameValue(pktBuf, sizeof(pktBuf), 0); + } + for (int j = 0; j < numBursts; j++) { for (int k = 0; k < numPackets; k++) { - int len; - - /*! \todo (HIGH) if pkt contents do not change across - pkts then don't call makePacket(), rather reuse the - previous */ - len = streamList[i]->makePacket(pktBuf, sizeof(pktBuf), + if (isVariable) + { + len = streamList[i]->frameValue(pktBuf, sizeof(pktBuf), j * numPackets + k); + } if (len > 0) { pktHdr.caplen = pktHdr.len = len; @@ -784,7 +761,6 @@ void PortInfo::PortCapture::run() { if (!capFile.open()) qFatal("Unable to open temp cap file"); - return; } qDebug("cap file = %s", capFile.fileName().toAscii().constData()); diff --git a/server/myservice.h b/server/myservice.h index ebd1db1..bafe9b9 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -46,9 +46,6 @@ class StreamInfo : public StreamBase public: StreamInfo(); ~StreamInfo(); - -private: - int makePacket(uchar *buf, int bufMaxSize, int n); }; From 3602d24767164745bb930a1c4ca077d5e194fb05 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Nov 2009 15:09:19 +0000 Subject: [PATCH 030/294] Fixes - LinkState is now updated in PortWindow as soon as a change is detected; the required minimal refactoring of the Port class usage has been done - Fixed a compiler warning in portgrouplist.cpp Others - PortStatsFilter: Ui change - added left and right arrow icons --- client/icons/arrow_left.png | Bin 0 -> 345 bytes client/ostinato.qrc | 1 + client/port.cpp | 6 ++-- client/port.h | 17 +++-------- client/portgroup.cpp | 57 ++++++++++++++++++++---------------- client/portgroup.h | 4 +-- client/portgrouplist.cpp | 27 +++++------------ client/portmodel.cpp | 41 ++++++++------------------ client/portmodel.h | 2 +- client/portstatsfilter.ui | 6 ++++ client/portstatsmodel.cpp | 2 +- client/streammodel.cpp | 2 +- 12 files changed, 71 insertions(+), 94 deletions(-) create mode 100644 client/icons/arrow_left.png diff --git a/client/icons/arrow_left.png b/client/icons/arrow_left.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc696781e6135d37b5bf2e98e46fd94f020c48d GIT binary patch literal 345 zcmV-f0jBq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaI icons/arrow_down.png + icons/arrow_left.png icons/arrow_right.png icons/arrow_up.png icons/bullet_error.png diff --git a/client/port.cpp b/client/port.cpp index d7e2b4c..87f0e07 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -187,9 +187,11 @@ void Port::updateStats(OstProto::PortStats *portStats) oldState = stats.state(); stats.MergeFrom(*portStats); -#if 0 + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); emit portDataChanged(mPortGroupId, mPortId); -#endif + } } diff --git a/client/port.h b/client/port.h index bdafdb9..9be12ed 100644 --- a/client/port.h +++ b/client/port.h @@ -1,18 +1,17 @@ #ifndef _PORT_H #define _PORT_H -#include +#include #include #include #include "stream.h" //class StreamModel; -class Port { +class Port : public QObject { - //friend class StreamModel; + Q_OBJECT -private: static uint mAllocStreamId; OstProto::Port d; OstProto::PortStats stats; @@ -28,6 +27,7 @@ private: uint newStreamId(); void updateStreamOrdinalsFromIndex(); void reorderStreamsByOrdinals(); + public: enum AdminStatus { AdminDisable, AdminEnable }; enum ControlMode { ControlShared, ControlExclusive }; @@ -50,13 +50,6 @@ public: ControlMode controlMode() { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } -#if 0 - void setName(QString &name) { d.name; } - void setName(const char* name) { mName = QString(name); } - void setDescription(QString &description) { mDescription = description; } - void setDescription(const char *description) - { mDescription = QString(description); } -#endif //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString &alias) { mUserAlias = alias; } //void setExclusive(bool flag); @@ -97,10 +90,8 @@ public: void updateStats(OstProto::PortStats *portStats); -#if 0 signals: void portDataChanged(int portGroupId, int portId); -#endif }; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 76888f3..9e17ae8 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -51,7 +51,7 @@ PortGroup::~PortGroup() void PortGroup::on_rpcChannel_stateChanged() { qDebug("state changed"); - emit portGroupDataChanged(this); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::on_rpcChannel_connected() @@ -60,7 +60,7 @@ void PortGroup::on_rpcChannel_connected() OstProto::PortIdList *portIdList; qDebug("connected\n"); - emit portGroupDataChanged(this); + emit portGroupDataChanged(mPortGroupId); qDebug("requesting portlist ..."); portIdList = new OstProto::PortIdList(); @@ -73,15 +73,18 @@ void PortGroup::on_rpcChannel_disconnected() { qDebug("disconnected\n"); emit portListAboutToBeChanged(mPortGroupId); - mPorts.clear(); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + emit portListChanged(mPortGroupId); - emit portGroupDataChanged(this); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { qDebug("error\n"); - emit portGroupDataChanged(this); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::when_configApply(int portIndex, uint *cookie) @@ -124,8 +127,8 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) qDebug("applying 'deleted streams' ..."); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); - mPorts[portIndex].getDeletedStreamsSinceLastSync(streamIdList); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); (*op)++; rpcController->Reset(); @@ -140,8 +143,8 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) qDebug("applying 'new streams' ..."); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); - mPorts[portIndex].getNewStreamsSinceLastSync(streamIdList); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); (*op)++; rpcController->Reset(); @@ -156,8 +159,8 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) qDebug("applying 'modified streams' ..."); - streamConfigList.mutable_port_id()->set_id(mPorts[portIndex].id()); - mPorts[portIndex].getModifiedStreamsSinceLastSync(streamConfigList); + streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); (*op)++; rpcController->Reset(); @@ -168,7 +171,7 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) case 3: qDebug("apply completed"); - mPorts[portIndex].when_syncComplete(); + mPorts[portIndex]->when_syncComplete(); delete cookie; break; @@ -195,8 +198,10 @@ void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) Port *p; p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); qDebug("before port append\n"); - mPorts.append(*p); + mPorts.append(p); } emit portListChanged(mPortGroupId); @@ -240,7 +245,7 @@ void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) id = portConfigList->port(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] - mPorts[id].updatePortConfig(portConfigList->mutable_port(i)); + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); } emit portListChanged(mPortGroupId); @@ -284,10 +289,10 @@ void PortGroup::getStreamIdList(int portIndex, Q_ASSERT(portIndex < numPorts()); - if (streamIdList->port_id().id() != mPorts[portIndex].id()) + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) { qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex].id(), + __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _next_port; // FIXME(MED): Partial RPC } @@ -298,14 +303,14 @@ void PortGroup::getStreamIdList(int portIndex, uint streamId; streamId = streamIdList->stream_id(i).id(); - mPorts[portIndex].insertStream(streamId); + mPorts[portIndex]->insertStream(streamId); } _next_port: // FIXME(HI): ideally we shd use signals/slots but this means // we will have to use Port* instead of Port with QList<> - // need to find a way for this - mPorts[portIndex].when_syncComplete(); + mPorts[portIndex]->when_syncComplete(); portIndex++; if (portIndex >= numPorts()) { @@ -322,7 +327,7 @@ _next_port: } _request: - portId.set_id(mPorts[portIndex].id()); + portId.set_id(mPorts[portIndex]->id()); streamIdList->Clear(); rpcController->Reset(); @@ -368,11 +373,11 @@ void PortGroup::getStreamConfigList(int portIndex, Q_ASSERT(portIndex < numPorts()); - if (streamConfigList->port_id().id() != mPorts[portIndex].id()) + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) { qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", __FUNCTION__, streamConfigList->port_id().id(), - mPorts[portIndex].id(), portIndex); + mPorts[portIndex]->id(), portIndex); goto _next_port; // FIXME(MED): Partial RPC } @@ -382,7 +387,7 @@ void PortGroup::getStreamConfigList(int portIndex, uint streamId; streamId = streamConfigList->stream(i).stream_id().id(); - mPorts[portIndex].updateStream(streamId, + mPorts[portIndex]->updateStream(streamId, streamConfigList->mutable_stream(i)); } @@ -403,13 +408,13 @@ _request: qDebug("requesting stream config list ..."); streamIdList.Clear(); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex].id()); - for (int j = 0; j < mPorts[portIndex].numStreams(); j++) + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) { OstProto::StreamId *s; s = streamIdList.add_stream_id(); - s->set_id(mPorts[portIndex].streamByIndex(j)->id()); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); } streamConfigList->Clear(); @@ -657,7 +662,7 @@ void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) id = portStatsList->port_stats(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] - mPorts[id].updateStats(portStatsList->mutable_port_stats(i)); + mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); } emit statsChanged(mPortGroupId); diff --git a/client/portgroup.h b/client/portgroup.h index 81363cc..c63d739 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -38,7 +38,7 @@ private: ::OstProto::PortIdList portIdList; public: // FIXME(HIGH): member access - QList mPorts; + QList mPorts; public: PortGroup(QHostAddress ip = QHostAddress::LocalHost, @@ -91,7 +91,7 @@ public: void processClearStatsAck(OstProto::Ack *ack); signals: - void portGroupDataChanged(PortGroup* portGroup, int portId = 0xFFFF); + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); void statsChanged(quint32 portGroupId); diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 4387aee..e5aa69e 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -32,36 +32,23 @@ bool PortGroupList::isPort(const QModelIndex& index) PortGroup& PortGroupList::portGroup(const QModelIndex& index) { - if (mPortGroupListModel.isPortGroup(index)) - { - //return *(mPortGroups[mPortGroupListModel.portGroupId(index)]); - return *(mPortGroups[index.row()]); - } -#if 0 // FIXME(MED) - else - return NULL; -#endif + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); } Port& PortGroupList::port(const QModelIndex& index) { - if (mPortGroupListModel.isPort(index)) - { - return (mPortGroups.at(index.parent().row())-> - mPorts[index.row()]); - } -#if 0 // FIXME(MED) - else - return NULL; -#endif + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); } void PortGroupList::addPortGroup(PortGroup &portGroup) { mPortGroupListModel.portGroupAboutToBeAppended(); - connect(&portGroup, SIGNAL(portGroupDataChanged(PortGroup*, int)), - &mPortGroupListModel, SLOT(when_portGroupDataChanged(PortGroup*, int))); + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); #if 0 connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 9397cc1..480b642 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -132,19 +132,19 @@ QVariant PortModel::data(const QModelIndex &index, int role) const return QString("Port %1: %2 [%3] (%4)"). arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()].id()). + parent.row())->mPorts[index.row()]->id()). arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()].name()). + parent.row())->mPorts[index.row()]->name()). arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()].description()); + parent.row())->mPorts[index.row()]->description()); } else if ((role == Qt::DecorationRole)) { DBG0("Exit PortModel data 5\n"); if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) return QVariant(); - switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()].linkState()) + switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) { case OstProto::LinkStateUnknown: return QIcon(":/icons/bullet_white.png"); @@ -198,7 +198,7 @@ QModelIndex PortModel::index (int row, int col, else { quint16 pg = parent.internalId() >> 16; - quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row).id(); + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); quint32 id = (pg << 16) | p; //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); @@ -264,21 +264,18 @@ quint32 PortModel::portId(const QModelIndex& index) // ---------------------------------------------- // Slots // ---------------------------------------------- -void PortModel::when_portGroupDataChanged(PortGroup* portGroup, int portId) +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) { QModelIndex index; + int row; - if (!pgl->mPortGroups.contains(portGroup)) - { - qDebug("when_portGroupDataChanged(): pg not in list - do nothing"); - return; - } + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); - qDebug("when_portGroupDataChanged pgid = %d", portGroup->id()); - qDebug("when_portGroupDataChanged idx = %d", pgl->mPortGroups.indexOf(portGroup)); - - index = createIndex(pgl->mPortGroups.indexOf(portGroup), 0, - (portGroup->id() << 16) | portId); emit dataChanged(index, index); } @@ -308,18 +305,6 @@ void PortModel::portGroupRemoved() endRemoveRows(); } -#if 0 -void PortModel::triggerLayoutAboutToBeChanged() -{ - emit layoutAboutToBeChanged(); -} - -void PortModel::triggerLayoutChanged() -{ - emit layoutChanged(); -} -#endif - void PortModel::when_portListChanged() { reset(); diff --git a/client/portmodel.h b/client/portmodel.h index de2b140..bd4b791 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -32,7 +32,7 @@ class PortModel : public QAbstractItemModel private slots: - void when_portGroupDataChanged(PortGroup *portGroup, int portId); + void when_portGroupDataChanged(int portGroupId, int portId); void portGroupAboutToBeAppended(); void portGroupAppended(); diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui index af9af02..61aa7a7 100644 --- a/client/portstatsfilter.ui +++ b/client/portstatsfilter.ui @@ -57,6 +57,9 @@ > + + :/icons/arrow_right.png + @@ -64,6 +67,9 @@ < + + :/icons/arrow_left.png + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 6d77945..ce75846 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -91,7 +91,7 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const { OstProto::PortStats stats; - stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx].getStats(); + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); switch(row) { diff --git a/client/streammodel.cpp b/client/streammodel.cpp index cba20e6..a3036fb 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -236,7 +236,7 @@ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) { qDebug("change to valid port"); quint16 pg = current.internalId() >> 16; - mCurrentPort = &pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; } reset(); } From c9563b50eb1b12efd294976ac4ea082af5ea1ed4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 16 Nov 2009 13:09:20 +0000 Subject: [PATCH 031/294] Features Sample Protocol completed - can be used as a template while implementing a new protocol --- common/abstractprotocol.h | 2 +- common/ostproto.pro | 12 +- common/protocol.proto | 1 + common/protocolmanager.cpp | 4 + common/sample.cpp | 347 +++++++++++++++++++++++++++++++++---- common/sample.h | 27 +++ common/sample.proto | 19 ++ common/sample.ui | 191 ++++++++++++++++++++ 8 files changed, 568 insertions(+), 35 deletions(-) create mode 100644 common/sample.proto create mode 100644 common/sample.ui diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index ccf1b12..c520a31 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -88,7 +88,7 @@ public: quint32 payloadProtocolId(ProtocolIdType type) const; virtual int fieldCount() const; - virtual int metaFieldCount() const; + int metaFieldCount() const; int frameFieldCount() const; virtual FieldFlags fieldFlags(int index) const; diff --git a/common/ostproto.pro b/common/ostproto.pro index 8ae7f99..38064b3 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -13,7 +13,8 @@ FORMS += \ vlan.ui \ ip4.ui \ tcp.ui \ - udp.ui + udp.ui \ + sample.ui PROTOS += \ protocol.proto \ mac.proto \ @@ -29,7 +30,8 @@ PROTOS += \ vlanstack.proto \ ip4.proto \ tcp.proto \ - udp.proto + udp.proto \ + sample.proto HEADERS += \ abstractprotocol.h \ comboprotocol.h \ @@ -50,7 +52,8 @@ HEADERS += \ vlanstack.h \ ip4.h \ tcp.h \ - udp.h + udp.h \ + sample.h SOURCES += \ abstractprotocol.cpp \ protocolmanager.cpp \ @@ -67,7 +70,8 @@ SOURCES += \ svlan.cpp \ ip4.cpp \ tcp.cpp \ - udp.cpp + udp.cpp \ + sample.cpp protobuf_decl.name = protobuf header protobuf_decl.input = PROTOS diff --git a/common/protocol.proto b/common/protocol.proto index 6557dbb..8b8a8f7 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -70,6 +70,7 @@ message Protocol { enum k { kMacFieldNumber = 51; kPayloadFieldNumber = 52; + kSampleFieldNumber = 53; kEth2FieldNumber = 121; kDot3FieldNumber = 122; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 8a9547d..3a9e254 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -2,6 +2,7 @@ #include "abstractprotocol.h" #include "protocol.pb.h" +#include "sample.h" #include "mac.h" #include "payload.h" #include "eth2.h" @@ -52,6 +53,9 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + populateNeighbourProtocols(); } diff --git a/common/sample.cpp b/common/sample.cpp index 63aaa7e..2477908 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -1,19 +1,14 @@ #include -#include #include "sample.h" -/*! \todo (MED) Complete the "sample" protocol and make it compilable so that - it can be used as an example for new protocols - */ - SampleConfigForm::SampleConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); } -SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent); +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { configForm = NULL; @@ -38,24 +33,55 @@ quint32 SampleProtocol::protocolNumber() const void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::sample)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()) + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id()->id() == protocolNumber() && + if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::sample)) data.MergeFrom(protocol.GetExtension(OstProto::sample)); } QString SampleProtocol::name() const { - return QString("Sample"); + return QString("Sample Protocol"); } QString SampleProtocol::shortName() const { - return QString("Sample"); + return QString("SAMPLE"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); } int SampleProtocol::fieldCount() const @@ -71,18 +97,26 @@ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const switch (index) { - case sample_normal: + case sample_a: + case sample_b: + case sample_payloadLength: break; - case sample_cksum: + case sample_checksum: flags |= FieldIsCksum; break; - case sample_meta: + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: flags |= FieldIsMeta; break; default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } @@ -94,55 +128,188 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, { switch (index) { - case sample_one: + case sample_a: { + int a = data.ab() >> 13; + switch(attrib) { case FieldName: - return QString("ONE"); + return QString("A"); case FieldValue: - return data.one(); + return a; case FieldTextValue: - return QString("%1").arg(data.one()); + return QString("%1").arg(a); case FieldFrameValue: - return QByteArray(1, (char)(data.one() & 0xF0)); + return QByteArray(1, (char) a); case FieldBitSize: - return 4; + return 3; default: break; } break; } - case sample_two: + case sample_b: { + int b = data.ab() & 0x1FFF; + switch(attrib) { case FieldName: - return QString("TWO"); + return QString("B"); case FieldValue: - return data.two(); + return b; case FieldTextValue: - return QString("%1").arg(data.two()); + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; - fv.resize(0); - qToBigEndian(FIXME, (uchar*) fv.data()); + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); return fv; } - return QByteArray(1, (char)(FIXME() & 0xF0)); case FieldBitSize: - return 4; + return 13; default: break; } break; } - // Meta fields - case sample_FIXME: + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + } + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + return QString("%1").arg(data.x()); + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + //return QString("%1").arg(data.y()); + return QString("%1").arg(data.y(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } @@ -152,10 +319,105 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, bool SampleProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { - // FIXME + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ return false; } +/*! + If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} QWidget* SampleProtocol::configWidget() { @@ -171,6 +433,21 @@ QWidget* SampleProtocol::configWidget() void SampleProtocol::loadConfigWidget() { configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + } void SampleProtocol::storeConfigWidget() @@ -178,5 +455,15 @@ void SampleProtocol::storeConfigWidget() bool isOk; configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); } diff --git a/common/sample.h b/common/sample.h index 6075262..a0e0ad3 100644 --- a/common/sample.h +++ b/common/sample.h @@ -6,6 +6,15 @@ #include "sample.pb.h" #include "ui_sample.h" +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + class SampleConfigForm : public QWidget, public Ui::Sample { Q_OBJECT @@ -21,6 +30,16 @@ private: SampleConfigForm *configForm; enum samplefield { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, sample_fieldCount }; @@ -36,6 +55,9 @@ public: virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual QString name() const; virtual QString shortName() const; @@ -47,6 +69,11 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..76213d4 --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,19 @@ +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 53; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + From ebc0403fde51df59bc205b6ee7c92345f626b1a6 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 23 Nov 2009 08:08:22 +0000 Subject: [PATCH 032/294] Features UserScript Protocol "first cut" checked-in. Needs rework. --- client/ostinato.pro | 2 +- common/ostproto.pro | 6 +- common/protocol.proto | 1 + common/protocolmanager.cpp | 7 +- common/userscript.cpp | 492 +++++++++++++++++++++++++++++++++++++ common/userscript.h | 160 ++++++++++++ common/userscript.proto | 14 ++ common/userscript.ui | 61 +++++ server/drone.pro | 2 +- 9 files changed, 740 insertions(+), 5 deletions(-) create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui diff --git a/client/ostinato.pro b/client/ostinato.pro index 707969e..ff54e47 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,6 +1,6 @@ TEMPLATE = app CONFIG += qt debug -QT += network +QT += network script INCLUDEPATH += "../rpc/" "../common/" LIBS += -lprotobuf win32:LIBS += -L"../common/debug" -lostproto diff --git a/common/ostproto.pro b/common/ostproto.pro index 38064b3..19121ca 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -1,6 +1,6 @@ TEMPLATE = lib CONFIG += qt staticlib -QT += network +QT += network script LIBS += \ -lprotobuf FORMS += \ @@ -14,6 +14,7 @@ FORMS += \ ip4.ui \ tcp.ui \ udp.ui \ + userscript.ui \ sample.ui PROTOS += \ protocol.proto \ @@ -31,6 +32,7 @@ PROTOS += \ ip4.proto \ tcp.proto \ udp.proto \ + userscript.proto \ sample.proto HEADERS += \ abstractprotocol.h \ @@ -53,6 +55,7 @@ HEADERS += \ ip4.h \ tcp.h \ udp.h \ + userscript.h \ sample.h SOURCES += \ abstractprotocol.cpp \ @@ -71,6 +74,7 @@ SOURCES += \ ip4.cpp \ tcp.cpp \ udp.cpp \ + userscript.cpp \ sample.cpp protobuf_decl.name = protobuf header diff --git a/common/protocol.proto b/common/protocol.proto index 8b8a8f7..30bec18 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -71,6 +71,7 @@ message Protocol { kMacFieldNumber = 51; kPayloadFieldNumber = 52; kSampleFieldNumber = 53; + kUserScriptFieldNumber = 54; kEth2FieldNumber = 121; kDot3FieldNumber = 122; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 3a9e254..7c8763d 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -2,7 +2,6 @@ #include "abstractprotocol.h" #include "protocol.pb.h" -#include "sample.h" #include "mac.h" #include "payload.h" #include "eth2.h" @@ -16,6 +15,8 @@ #include "ip4.h" #include "tcp.h" #include "udp.h" +#include "userscript.h" +#include "sample.h" ProtocolManager OstProtocolManager; @@ -53,6 +54,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, (void*) SampleProtocol::createInstance); @@ -64,7 +67,7 @@ void ProtocolManager::registerProtocol(int protoNumber, { AbstractProtocol *p; - //! \todo (MED) validate incoming params for duplicates with existing + Q_ASSERT(!factory.contains(protoNumber)); factory.insert(protoNumber, protoInstanceCreator); diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..5cb792c --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,492 @@ +#include "userscript.h" + +#include +#include +#include +#include + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), _protocol(protocol) +{ + setupUi(this); +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ +} + +void UserScriptConfigForm::on_compileButton_clicked(bool checked) +{ + _protocol->storeConfigWidget(); + if (_protocol->userScriptErrorLineNumber() >= 0) + { + statusLabel->setText("Fail"); + QMessageBox::critical(this, "Error", + QString("Line %1: %2").arg( + _protocol->userScriptErrorLineNumber()).arg( + _protocol->userScriptErrorText())); + } + else + { + statusLabel->setText("Success"); + } +} + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + _userProtocol(this) +{ + configForm = NULL; + _isUpdated = false; + _errorLineNumber = -1; + + _userProtocolScriptValue = _scriptEngine.newQObject(&_userProtocol); + _userProtocolScriptValue.setProperty("protocolId", _scriptEngine.newObject()); + _scriptEngine.globalObject().setProperty("protocol", _userProtocolScriptValue); + + + QScriptValue meta = _scriptEngine.newQMetaObject(_userProtocol.metaObject()); + _scriptEngine.globalObject().setProperty("Protocol", meta); + +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript}").arg(_userProtocol.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script}").arg(_userProtocol.shortName()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + if (_userProtocol._protocolId.contains(static_cast(type))) + return _userProtocol._protocolId.value(static_cast(type)); + else + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + { + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + QScriptValue userFunction; + QByteArray fv; + + evaluateUserScript(); // FIXME: don't call everytime! + + if (_userProtocol.isProtocolFrameFixed()) + { + fv = _userProtocol.protocolFrameFixedValue(); + if (fv.size()) + return fv; + else + return QByteArray(4, 0); // FIXME + } + + userFunction = _userProtocolScriptValue.property( + "protocolFrameValue"); + + qDebug("userscript protoFrameVal(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (userFunction.isValid() && userFunction.isFunction()) + { + QScriptValue userValue; + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() + << QScriptValue(&_scriptEngine, streamIndex)); + + qDebug("userscript value: isValid:%d/isArray:%d", + userValue.isValid(), userValue.isArray()); + + if (userValue.isArray()) + { + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + } + } + + if (fv.size()) + return fv; + else + return QByteArray(4, 0); // FIXME + } + //case FieldBitSize: + //return 3; + default: + break; + } + break; + + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return _userProtocol.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return _userProtocol.isProtocolFrameSizeVariable(); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +bool UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + + _errorLineNumber = -1; + _userProtocol.reset(); + _userProtocolScriptValue.setProperty("protocolFrameValue", QScriptValue()); + + _scriptEngine.evaluate( + fieldData(userScript_program, FieldValue).toString()); + if (_scriptEngine.hasUncaughtException()) + goto _error_exception; + + userFunction = _userProtocolScriptValue.property( + "protocolFrameValue"); + qDebug("userscript eval protoFrameVal(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + if (!userFunction.isValid()) + goto _error_frame_value_not_set; + + if (userFunction.isArray()) + { + QList pktBuf; + + userValue = userFunction; + _userProtocol.setProtocolFrameValueVariable(false); + + qScriptValueToSequence(userValue, pktBuf); + _userProtocol.setProtocolFrameFixedValue(pktBuf); + } + else if (userFunction.isFunction()) + { + userValue = userFunction.call(); + if (_scriptEngine.hasUncaughtException()) + goto _error_exception; + qDebug("userscript eval value: isValid:%d/isArray:%d", + userValue.isValid(), userValue.isArray()); + if (!userValue.isArray()) + goto _error_not_an_array; + + if (_userProtocol.isProtocolFrameFixed()) + { + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + _userProtocol.setProtocolFrameFixedValue(pktBuf); + } + } + else + goto _error_frame_value_type; + + + userValue = _userProtocolScriptValue.property("protocolId"); + + if (userValue.isValid() && userValue.isObject()) + { + QMetaEnum meta; + QScriptValue userProtocolId; + + meta = _userProtocol.metaObject()->enumerator( + _userProtocol.metaObject()->indexOfEnumerator("ProtocolIdType")); + for (int i = 0; i < meta.keyCount(); i++) + { + userProtocolId = userValue.property(meta.key(i)); + + qDebug("userscript protoId: isValid:%d/isNumber:%d", + userProtocolId.isValid(), + userProtocolId.isNumber()); + + if (userProtocolId.isValid() && userProtocolId.isNumber()) + _userProtocol._protocolId.insert( + meta.value(i), userProtocolId.toUInt32()); + } + } + + _isUpdated = true; + return true; + +_error_not_an_array: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("property 'protocolFrameValue' returns invalid array"); + goto _error; + +_error_frame_value_type: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("property 'protocolFrameValue' is neither an array nor a function"); + goto _error; + +_error_frame_value_not_set: + _errorLineNumber = userScriptLineCount(); + _errorText = QString("mandatory property 'protocolFrameValue' is not set"); + goto _error; + +_error_exception: + _errorLineNumber = _scriptEngine.uncaughtExceptionLineNumber(); + _errorText = _scriptEngine.uncaughtException().toString(); + goto _error; + +_error: + _userProtocol.reset(); + return false; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return _errorLineNumber; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return _errorText; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// --------------------------------------------------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : _parent (parent) +{ + reset(); +} + +bool UserProtocol::isProtocolFrameFixed() const +{ + return !isProtocolFrameValueVariable() && !isProtocolFrameSizeVariable(); +} + +const QByteArray& UserProtocol::protocolFrameFixedValue() const +{ + return _protocolFrameFixedValue; +} + +void UserProtocol::setProtocolFrameFixedValue(const QByteArray& value) +{ + _protocolFrameFixedValue = value; +} + +void UserProtocol::setProtocolFrameFixedValue(const QList& value) +{ + _protocolFrameFixedValue.resize(value.size()); + for (int i = 0; i < value.size(); i++) + _protocolFrameFixedValue[i] = value.at(i) & 0xFF; +} + +void UserProtocol::reset() +{ + _name = QString(); + _shortName = QString(); + _protocolId.clear(); + _protocolFrameValueVariable = false; + _protocolFrameSizeVariable = false; + _protocolFrameFixedValue.resize(0); +} + +QString UserProtocol::name() const +{ + return _name; +} + +void UserProtocol::setName(QString &name) +{ + _name = name; +} + +QString UserProtocol::shortName() const +{ + return _shortName; +} + +void UserProtocol::setShortName(QString &shortName) +{ + _shortName = shortName; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return _protocolFrameValueVariable; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + qDebug("%s: %d", __FUNCTION__, variable); + _protocolFrameValueVariable = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return _protocolFrameSizeVariable; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + _protocolFrameSizeVariable = variable; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return _parent->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return _parent->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return _parent->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return _parent->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return _parent->isProtocolFramePayloadSizeVariable(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return _parent->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return _parent->protocolFramePayloadCksum(streamIndex, cksumType); +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..504e285 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,160 @@ +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + friend class UserScriptProtocol; + + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(QString shortName READ shortName WRITE setShortName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp + }; + + UserProtocol(AbstractProtocol *parent); + + bool isProtocolFrameFixed() const; + const QByteArray& protocolFrameFixedValue() const; + void setProtocolFrameFixedValue(const QByteArray& value); + void setProtocolFrameFixedValue(const QList& value); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + QString shortName() const; + void setShortName(QString &shortName); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *_parent; + + QString _name; + QString _shortName; + QMap _protocolId; + bool _protocolFrameValueVariable; + bool _protocolFrameSizeVariable; + QByteArray _protocolFrameFixedValue; + +}; + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + UserScriptProtocol *_protocol; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + +class UserScriptProtocol : public AbstractProtocol +{ + friend class UserScriptConfigForm; + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + bool evaluateUserScript() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + + int userScriptLineCount() const; + + + OstProto::UserScript data; + UserScriptConfigForm *configForm; + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + + mutable QScriptEngine _scriptEngine; + mutable UserProtocol _userProtocol; + mutable QScriptValue _userProtocolScriptValue; + + mutable bool _isUpdated; + mutable int _errorLineNumber; + mutable QString _errorText; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..3f26cd2 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,14 @@ +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 54; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..a5dde73 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,61 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + + Compilation Status: + + + + + + + Unknown + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Compile + + + + + + + + + + diff --git a/server/drone.pro b/server/drone.pro index c42d9a4..f569b4d 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,6 +1,6 @@ TEMPLATE = app CONFIG += qt debug -QT += network +QT += network script DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "../rpc" LIBS += -lprotobuf From 4c5b8faff7c1784e373578b6af3a352da5df6707 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 28 Nov 2009 08:35:05 +0000 Subject: [PATCH 033/294] Fixes/Rework - UserScript Protocol reworked. Needs more testing. May need a significant rewrite in the future, but for now this will have to do - therefore it has been marked "EXPERIMENTAL" for now --- common/abstractprotocol.cpp | 2 + common/ip4.h | 5 +- common/userscript.cpp | 556 +++++++++++++++++++++--------------- common/userscript.h | 64 +++-- common/userscript.ui | 87 +++--- 5 files changed, 413 insertions(+), 301 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 3dd58ae..025a745 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -287,6 +287,8 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) if (!flags.testFlag(FieldIsMeta)) { bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; Q_ASSERT(bits > 0); if (forCksum && flags.testFlag(FieldIsCksum)) diff --git a/common/ip4.h b/common/ip4.h index 34f4c37..e3e1a23 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -80,11 +80,12 @@ public: int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; virtual bool isProtocolFrameValueVariable() const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/userscript.cpp b/common/userscript.cpp index 5cb792c..89191fb 100644 --- a/common/userscript.cpp +++ b/common/userscript.cpp @@ -1,53 +1,70 @@ #include "userscript.h" -#include #include -#include -#include + +// +// -------------------- UserScriptConfigForm -------------------- +// UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, - QWidget *parent) : QWidget(parent), _protocol(protocol) + QWidget *parent) : QWidget(parent), protocol_(protocol) { setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } } void UserScriptConfigForm::on_programEdit_textChanged() { + compileButton->setEnabled(true); } void UserScriptConfigForm::on_compileButton_clicked(bool checked) { - _protocol->storeConfigWidget(); - if (_protocol->userScriptErrorLineNumber() >= 0) + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) { - statusLabel->setText("Fail"); QMessageBox::critical(this, "Error", - QString("Line %1: %2").arg( - _protocol->userScriptErrorLineNumber()).arg( - _protocol->userScriptErrorText())); - } - else - { - statusLabel->setText("Success"); + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); } + updateStatus(); } +// +// -------------------- UserScriptProtocol -------------------- +// + UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent), - _userProtocol(this) + userProtocol_(this) { configForm = NULL; - _isUpdated = false; - _errorLineNumber = -1; + isScriptValid_ = false; + errorLineNumber_ = 0; - _userProtocolScriptValue = _scriptEngine.newQObject(&_userProtocol); - _userProtocolScriptValue.setProperty("protocolId", _scriptEngine.newObject()); - _scriptEngine.globalObject().setProperty("protocol", _userProtocolScriptValue); - - - QScriptValue meta = _scriptEngine.newQMetaObject(_userProtocol.metaObject()); - _scriptEngine.globalObject().setProperty("Protocol", meta); + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); } UserScriptProtocol::~UserScriptProtocol() @@ -83,23 +100,42 @@ void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) QString UserScriptProtocol::name() const { - return QString("%1:{UserScript}").arg(_userProtocol.name()); + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); } QString UserScriptProtocol::shortName() const { - return QString("%1:{Script}").arg(_userProtocol.shortName()); + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); } quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const { - if (_userProtocol._protocolId.contains(static_cast(type))) - return _userProtocol._protocolId.value(static_cast(type)); - else - return AbstractProtocol::protocolId(type); + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); } -int UserScriptProtocol::fieldCount() const +int UserScriptProtocol::fieldCount() const { return userScript_fieldCount; } @@ -109,79 +145,54 @@ QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, { switch (index) { - case userScript_program: + case userScript_program: + + switch(attrib) { - switch(attrib) - { - case FieldName: - return QString("UserProtocol"); + case FieldName: + return QString("UserProtocol"); - case FieldValue: - case FieldTextValue: - return QString().fromStdString(data.program()); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); - case FieldFrameValue: - { - QScriptValue userFunction; - QByteArray fv; + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); - evaluateUserScript(); // FIXME: don't call everytime! + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); - if (_userProtocol.isProtocolFrameFixed()) - { - fv = _userProtocol.protocolFrameFixedValue(); - if (fv.size()) - return fv; - else - return QByteArray(4, 0); // FIXME - } + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); - userFunction = _userProtocolScriptValue.property( - "protocolFrameValue"); + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); - qDebug("userscript protoFrameVal(): isValid:%d/isFunc:%d", - userFunction.isValid(), userFunction.isFunction()); + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); - if (userFunction.isValid() && userFunction.isFunction()) - { - QScriptValue userValue; + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); - userValue = userFunction.call(QScriptValue(), - QScriptValueList() - << QScriptValue(&_scriptEngine, streamIndex)); - - qDebug("userscript value: isValid:%d/isArray:%d", - userValue.isValid(), userValue.isArray()); - - if (userValue.isArray()) - { - QList pktBuf; - - qScriptValueToSequence(userValue, pktBuf); - - fv.resize(pktBuf.size()); - for (int i = 0; i < pktBuf.size(); i++) - fv[i] = pktBuf.at(i) & 0xFF; - } - } - - if (fv.size()) - return fv; - else - return QByteArray(4, 0); // FIXME - } - //case FieldBitSize: - //return 3; - default: - break; - } - break; + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + return fv; } default: - qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, - index); break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); @@ -212,14 +223,65 @@ _exit: return isOk; } +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + bool UserScriptProtocol::isProtocolFrameValueVariable() const { - return _userProtocol.isProtocolFrameValueVariable(); + return userProtocol_.isProtocolFrameValueVariable(); } bool UserScriptProtocol::isProtocolFrameSizeVariable() const { - return _userProtocol.isProtocolFrameSizeVariable(); + return userProtocol_.isProtocolFrameSizeVariable(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } QWidget* UserScriptProtocol::configWidget() @@ -248,118 +310,186 @@ void UserScriptProtocol::storeConfigWidget() evaluateUserScript(); } -bool UserScriptProtocol::evaluateUserScript() const +void UserScriptProtocol::evaluateUserScript() const { QScriptValue userFunction; QScriptValue userValue; + QString property; - _errorLineNumber = -1; - _userProtocol.reset(); - _userProtocolScriptValue.setProperty("protocolFrameValue", QScriptValue()); + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); - _scriptEngine.evaluate( - fieldData(userScript_program, FieldValue).toString()); - if (_scriptEngine.hasUncaughtException()) + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) goto _error_exception; - userFunction = _userProtocolScriptValue.property( - "protocolFrameValue"); - qDebug("userscript eval protoFrameVal(): isValid:%d/isFunc:%d", - userFunction.isValid(), userFunction.isFunction()); + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + if (!userFunction.isValid()) - goto _error_frame_value_not_set; - - if (userFunction.isArray()) { - QList pktBuf; - - userValue = userFunction; - _userProtocol.setProtocolFrameValueVariable(false); - - qScriptValueToSequence(userValue, pktBuf); - _userProtocol.setProtocolFrameFixedValue(pktBuf); + errorText_ = property + QString(" not set"); + goto _error_exit; } - else if (userFunction.isFunction()) + + if (!userFunction.isFunction()) { - userValue = userFunction.call(); - if (_scriptEngine.hasUncaughtException()) - goto _error_exception; - qDebug("userscript eval value: isValid:%d/isArray:%d", + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), userValue.isValid(), userValue.isArray()); - if (!userValue.isArray()) - goto _error_not_an_array; - if (_userProtocol.isProtocolFrameFixed()) - { - QList pktBuf; - - qScriptValueToSequence(userValue, pktBuf); - _userProtocol.setProtocolFrameFixedValue(pktBuf); - } - } - else - goto _error_frame_value_type; - - - userValue = _userProtocolScriptValue.property("protocolId"); - - if (userValue.isValid() && userValue.isObject()) + if (!userValue.isArray()) { - QMetaEnum meta; - QScriptValue userProtocolId; - - meta = _userProtocol.metaObject()->enumerator( - _userProtocol.metaObject()->indexOfEnumerator("ProtocolIdType")); - for (int i = 0; i < meta.keyCount(); i++) - { - userProtocolId = userValue.property(meta.key(i)); - - qDebug("userscript protoId: isValid:%d/isNumber:%d", - userProtocolId.isValid(), - userProtocolId.isNumber()); - - if (userProtocolId.isValid() && userProtocolId.isNumber()) - _userProtocol._protocolId.insert( - meta.value(i), userProtocolId.toUInt32()); - } + errorText_ = property + QString(" does not return an array"); + goto _error_exit; } - _isUpdated = true; - return true; + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); -_error_not_an_array: - _errorLineNumber = userScriptLineCount(); - _errorText = QString("property 'protocolFrameValue' returns invalid array"); - goto _error; + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); -_error_frame_value_type: - _errorLineNumber = userScriptLineCount(); - _errorText = QString("property 'protocolFrameValue' is neither an array nor a function"); - goto _error; + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } -_error_frame_value_not_set: - _errorLineNumber = userScriptLineCount(); - _errorText = QString("mandatory property 'protocolFrameValue' is not set"); - goto _error; + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; _error_exception: - _errorLineNumber = _scriptEngine.uncaughtExceptionLineNumber(); - _errorText = _scriptEngine.uncaughtException().toString(); - goto _error; + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); -_error: - _userProtocol.reset(); - return false; +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; } int UserScriptProtocol::userScriptErrorLineNumber() const { - return _errorLineNumber; + return errorLineNumber_; } QString UserScriptProtocol::userScriptErrorText() const { - return _errorText; + return errorText_; } int UserScriptProtocol::userScriptLineCount() const @@ -369,124 +499,92 @@ int UserScriptProtocol::userScriptLineCount() const } // -// --------------------------------------------------------------- +// -------------------- UserProtocol -------------------- // UserProtocol::UserProtocol(AbstractProtocol *parent) - : _parent (parent) + : parent_ (parent) { reset(); } -bool UserProtocol::isProtocolFrameFixed() const -{ - return !isProtocolFrameValueVariable() && !isProtocolFrameSizeVariable(); -} - -const QByteArray& UserProtocol::protocolFrameFixedValue() const -{ - return _protocolFrameFixedValue; -} - -void UserProtocol::setProtocolFrameFixedValue(const QByteArray& value) -{ - _protocolFrameFixedValue = value; -} - -void UserProtocol::setProtocolFrameFixedValue(const QList& value) -{ - _protocolFrameFixedValue.resize(value.size()); - for (int i = 0; i < value.size(); i++) - _protocolFrameFixedValue[i] = value.at(i) & 0xFF; -} - void UserProtocol::reset() { - _name = QString(); - _shortName = QString(); - _protocolId.clear(); - _protocolFrameValueVariable = false; - _protocolFrameSizeVariable = false; - _protocolFrameFixedValue.resize(0); + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; } QString UserProtocol::name() const { - return _name; + return name_; } void UserProtocol::setName(QString &name) { - _name = name; -} - -QString UserProtocol::shortName() const -{ - return _shortName; -} - -void UserProtocol::setShortName(QString &shortName) -{ - _shortName = shortName; + name_ = name; } bool UserProtocol::isProtocolFrameValueVariable() const { - return _protocolFrameValueVariable; + return protocolFrameValueVariable_; } void UserProtocol::setProtocolFrameValueVariable(bool variable) { - qDebug("%s: %d", __FUNCTION__, variable); - _protocolFrameValueVariable = variable; + protocolFrameValueVariable_ = variable; } bool UserProtocol::isProtocolFrameSizeVariable() const { - return _protocolFrameSizeVariable; + return protocolFrameSizeVariable_; } void UserProtocol::setProtocolFrameSizeVariable(bool variable) { - _protocolFrameSizeVariable = variable; + protocolFrameSizeVariable_ = variable; } quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const { - return _parent->payloadProtocolId( + return parent_->payloadProtocolId( static_cast(type)); } int UserProtocol::protocolFrameOffset(int streamIndex) const { - return _parent->protocolFrameOffset(streamIndex); + return parent_->protocolFrameOffset(streamIndex); } int UserProtocol::protocolFramePayloadSize(int streamIndex) const { - return _parent->protocolFramePayloadSize(streamIndex); + return parent_->protocolFramePayloadSize(streamIndex); } bool UserProtocol::isProtocolFramePayloadValueVariable() const { - return _parent->isProtocolFramePayloadValueVariable(); + return parent_->isProtocolFramePayloadValueVariable(); } bool UserProtocol::isProtocolFramePayloadSizeVariable() const { - return _parent->isProtocolFramePayloadSizeVariable(); + return parent_->isProtocolFramePayloadSizeVariable(); } quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, AbstractProtocol::CksumType cksumType) const { - return _parent->protocolFrameHeaderCksum(streamIndex, cksumType); + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); } quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, AbstractProtocol::CksumType cksumType) const { - return _parent->protocolFramePayloadCksum(streamIndex, cksumType); + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; } /* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h index 504e285..3f51142 100644 --- a/common/userscript.h +++ b/common/userscript.h @@ -12,13 +12,11 @@ class UserScriptProtocol; class UserProtocol : public QObject { - friend class UserScriptProtocol; - Q_OBJECT; Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); Q_PROPERTY(QString name READ name WRITE setName); - Q_PROPERTY(QString shortName READ shortName WRITE setShortName); Q_PROPERTY(bool protocolFrameValueVariable READ isProtocolFrameValueVariable WRITE setProtocolFrameValueVariable); @@ -34,20 +32,20 @@ public: ProtocolIdIp = AbstractProtocol::ProtocolIdIp }; - UserProtocol(AbstractProtocol *parent); + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; - bool isProtocolFrameFixed() const; - const QByteArray& protocolFrameFixedValue() const; - void setProtocolFrameFixedValue(const QByteArray& value); - void setProtocolFrameFixedValue(const QList& value); + UserProtocol(AbstractProtocol *parent); public slots: void reset(); QString name() const; void setName(QString &name); - QString shortName() const; - void setShortName(QString &shortName); bool isProtocolFrameValueVariable() const; void setProtocolFrameValueVariable(bool variable); @@ -67,17 +65,15 @@ public slots: AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; private: - AbstractProtocol *_parent; - - QString _name; - QString _shortName; - QMap _protocolId; - bool _protocolFrameValueVariable; - bool _protocolFrameSizeVariable; - QByteArray _protocolFrameFixedValue; + AbstractProtocol *parent_; + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; }; + + class UserScriptConfigForm : public QWidget, public Ui::UserScript { Q_OBJECT @@ -86,7 +82,8 @@ public: UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); private: - UserScriptProtocol *_protocol; + void updateStatus(); + UserScriptProtocol *protocol_; private slots: void on_programEdit_textChanged(); @@ -94,9 +91,9 @@ private slots: }; + class UserScriptProtocol : public AbstractProtocol { - friend class UserScriptConfigForm; public: UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); @@ -121,23 +118,26 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual int protocolFrameSize(int streamIndex = 0) const; + virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); -private: - bool evaluateUserScript() const; + void evaluateUserScript() const; + bool isScriptValid() const; int userScriptErrorLineNumber() const; QString userScriptErrorText() const; +private: int userScriptLineCount() const; - - OstProto::UserScript data; - UserScriptConfigForm *configForm; enum userScriptfield { // Frame Fields @@ -145,14 +145,16 @@ private: userScript_fieldCount }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; - mutable QScriptEngine _scriptEngine; - mutable UserProtocol _userProtocol; - mutable QScriptValue _userProtocolScriptValue; + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; - mutable bool _isUpdated; - mutable int _errorLineNumber; - mutable QString _errorText; + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; }; #endif diff --git a/common/userscript.ui b/common/userscript.ui index a5dde73..e18e024 100644 --- a/common/userscript.ui +++ b/common/userscript.ui @@ -12,47 +12,56 @@ Form - - + + - - - - - - Compilation Status: - - - - - - - Unknown - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Compile - - - - + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + From bb6a9235c31027bbbb0773aaddae29c635be514e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 29 Nov 2009 16:32:31 +0000 Subject: [PATCH 034/294] Features - Ostinato Client - will start the server as a child process at startup and terminate it at exit - Ostinato Server (Drone) - is now a system tray application - if not able to bind to a IP/Port successfully, informs the user and exits - the GUI is now nothing more than a TextLabel Others - If a getStats() request is pending, the client will not queue up any more requests till a reply is received for the pending one - Nitpicks in the Payload protocol Widget, PortsWindow Widget --- client/main.cpp | 3 +- client/mainwindow.cpp | 14 ++- client/mainwindow.h | 12 ++- client/portgroup.cpp | 6 ++ client/portgroup.h | 1 + client/portswindow.cpp | 1 - client/portswindow.ui | 3 + common/payload.ui | 135 +++++++++++++++++---------- rpc/rpcserver.cpp | 27 ++++-- rpc/rpcserver.h | 4 +- server/abstracthost.h | 14 --- server/drone.cpp | 79 +++++++++++++--- server/drone.h | 41 ++++++--- server/drone.pro | 1 + server/drone.ui | 182 ++++++++++++++++++++++++------------- server/drone_main.cpp | 17 +++- server/icons/portgroup.png | Bin 0 -> 667 bytes server/myservice.cpp | 5 +- server/myservice.h | 6 +- 19 files changed, 367 insertions(+), 184 deletions(-) delete mode 100644 server/abstracthost.h create mode 100644 server/icons/portgroup.png diff --git a/client/main.cpp b/client/main.cpp index 34a9f8c..2f9b385 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,6 +1,7 @@ -#include #include "mainwindow.h" +#include + int main(int argc, char* argv[]) { QApplication app(argc, argv); diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 0a0e0fb..9b2d12a 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -1,17 +1,25 @@ #include "mainwindow.h" -#include "portgrouplist.h" #if 0 #include "dbgthread.h" #endif +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" #include "ui_about.h" +#include +#include + PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) { + localServer_ = new QProcess(this); + localServer_->start("drone.exe"); + pgl = new PortGroupList; portsWindow = new PortsWindow(pgl, this); @@ -37,6 +45,10 @@ MainWindow::MainWindow(QWidget *parent) MainWindow::~MainWindow() { + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; } void MainWindow::on_actionHelpAbout_triggered() diff --git a/client/mainwindow.h b/client/mainwindow.h index a5d31a6..5132385 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -1,19 +1,21 @@ #ifndef _MAIN_WINDOW_H #define _MAIN_WINDOW_H -#include -#include - #include "ui_mainwindow.h" +#include -#include "portswindow.h" -#include "portstatswindow.h" +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; class MainWindow : public QMainWindow, private Ui::MainWindow { Q_OBJECT private: + QProcess *localServer_; PortsWindow *portsWindow; PortStatsWindow *statsWindow; QDockWidget *portsDock; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 9e17ae8..4a2843c 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -21,6 +21,7 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) */ rpcController = new PbRpcController; rpcControllerStats = new PbRpcController; + isGetStatsPending_ = false; serviceStub = new OstProto::OstService::Stub(rpcChannel, OstProto::OstService::STUB_OWNS_CHANNEL); @@ -640,8 +641,12 @@ void PortGroup::getPortStats() if (state() != QAbstractSocket::ConnectedState) return; + if (isGetStatsPending_) + return; + portStatsList = new OstProto::PortStatsList; rpcControllerStats->Reset(); + isGetStatsPending_ = true; serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); } @@ -669,6 +674,7 @@ void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) _error_exit: delete portStatsList; + isGetStatsPending_ = false; } void PortGroup::clearPortStats(QList *portList) diff --git a/client/portgroup.h b/client/portgroup.h index c63d739..f4d8e99 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -34,6 +34,7 @@ private: PbRpcChannel *rpcChannel; PbRpcController *rpcController; PbRpcController *rpcControllerStats; + bool isGetStatsPending_; ::OstProto::OstService::Stub *serviceStub; ::OstProto::PortIdList portIdList; diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 7d9bbc4..54d4b8b 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -71,7 +71,6 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) PortsWindow::~PortsWindow() { - delete plm; } void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) diff --git a/client/portswindow.ui b/client/portswindow.ui index 1ed9485..9e461a2 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -18,6 +18,9 @@ Qt::Horizontal + + false + Qt::ActionsContextMenu diff --git a/common/payload.ui b/common/payload.ui index c0a1b31..a7ff9a2 100644 --- a/common/payload.ui +++ b/common/payload.ui @@ -5,63 +5,100 @@ 0 0 - 142 - 98 + 299 + 114 Form - - - - - Data Pattern + + + + + Type + + + cmbPatternMode - - - - - - Fixed Word - - - - - Increment Byte - - - - - Decrement Byte - - - - - Random - - - - - - - - >HH HH HH HH; - - - - - - 11 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 039fc83..3d5a10c 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -27,14 +27,25 @@ bool RpcServer::registerService(::google::protobuf::Service *service, connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); if (!server->listen(QHostAddress::Any, tcpPortNum)) { - LogInt(tr("Unable to start the server: %1").arg(server->errorString())); + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); return false; } - LogInt(tr("The server is running on %1:%2").arg(server->serverAddress().toString()).arg(server->serverPort())); + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); return true; } +QString RpcServer::errorString() +{ + return errorString_; +} + void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) { QIODevice *blob; @@ -111,7 +122,7 @@ void RpcServer::when_newConnection() { QTcpSocket *sock; - LogInt(tr("already connected, no new connections will be accepted\n")); + qDebug("already connected, no new connections will be accepted"); // Accept and close connection //! \todo (MED) Send reason msg to client @@ -122,7 +133,9 @@ void RpcServer::when_newConnection() } clientSock = server->nextPendingConnection(); - LogInt(tr("accepting new connection from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); connect(clientSock, SIGNAL(readyRead()), this, SLOT(when_dataAvail())); @@ -137,7 +150,9 @@ _exit: void RpcServer::when_disconnected() { - LogInt(tr("connection closed from %1:%2").arg(clientSock->peerAddress().toString()).arg(clientSock->peerPort())); + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); clientSock->deleteLater(); clientSock = NULL; @@ -145,7 +160,7 @@ void RpcServer::when_disconnected() void RpcServer::when_error(QAbstractSocket::SocketError socketError) { - LogInt(clientSock->errorString()); + qDebug("%s", clientSock->errorString().toAscii().constData()); } void RpcServer::when_dataAvail() diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index d93b08a..619cb2d 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -23,8 +23,7 @@ class RpcServer : public QObject bool isPending; int pendingMethodId; - - void LogInt (QString log) {qDebug("%s", log.toAscii().data());} + QString errorString_; public: RpcServer(); //! \todo (LOW) use 'parent' param @@ -32,6 +31,7 @@ public: bool registerService(::google::protobuf::Service *service, quint16 tcpPortNum); + QString errorString(); void done(::google::protobuf::Message *resp, PbRpcController *controller); private slots: diff --git a/server/abstracthost.h b/server/abstracthost.h deleted file mode 100644 index 862666c..0000000 --- a/server/abstracthost.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _ABSTRACT_HOST -#define _ABSTRACT_HOST - -class AbstractHost -{ - public: - virtual void Log(const char* str) = 0; -#if 0 // PB - virtual int SendMsg(const void* msg, int size) = 0; -#endif -}; - -#endif - diff --git a/server/drone.cpp b/server/drone.cpp index 1f94d7e..c84aec5 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -1,23 +1,76 @@ #include "drone.h" +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + extern int myport; -Drone::Drone(QDialog *parent) - : QDialog(parent) +Drone::Drone(QWidget *parent) + : QWidget(parent) { - ui.setupUi(this); + setupUi(this); - rpcServer = new RpcServer(); - service = new MyService(this); - rpcServer->registerService(service, myport ? myport : 7878); -} - -void Drone::Log(const char* str) -{ - ui.teLog->append(QString(str)); + rpcServer = new RpcServer(); + service = new MyService(); } -void Drone::LogInt(const QString &str) +Drone::~Drone() { - ui.teLog->append(str); + trayIcon_->hide(); + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } } diff --git a/server/drone.h b/server/drone.h index 31574a6..5930dd0 100644 --- a/server/drone.h +++ b/server/drone.h @@ -1,26 +1,37 @@ #ifndef _DRONE_H #define _DRONE_H -#include -#include - #include "ui_drone.h" -#include "abstracthost.h" -#include "rpcserver.h" -#include "myservice.h" -class Drone : public QDialog, AbstractHost +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone { Q_OBJECT - public: - Ui::Drone ui; - Drone(QDialog *parent = 0); - void Log(const char *msg); +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - private: - RpcServer *rpcServer; - OstProto::OstService *service; - void LogInt(const QString &msg); }; #endif diff --git a/server/drone.pro b/server/drone.pro index f569b4d..a5cd2bb 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -11,6 +11,7 @@ unix:LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui SOURCES += drone_main.cpp drone.cpp diff --git a/server/drone.ui b/server/drone.ui index 5caa184..73fde04 100644 --- a/server/drone.ui +++ b/server/drone.ui @@ -1,69 +1,82 @@ Drone - + 0 0 - 400 - 300 + 268 + 216 Drone + + :/icons/portgroup.png + - - - 1 + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter - - - Status - - - - - 160 - 100 - 46 - 14 - - - - TODO - - - - - - Log - - - - - - true - - - false - - - - - + + + + (Server) + + + Qt::AlignCenter + + + + + + + TODO: Info/Status here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + - - - - Clear Log - - - @@ -84,41 +97,82 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + Show + + + + + Exit + + - + + + pushButton - clicked(bool) - Drone - accept() + clicked() + actionExit + trigger() - 341 - 279 + 134 + 194 - 226 - 268 + -1 + -1 - pbClearLog - clicked() - teLog - clear() + actionShow + triggered() + Drone + showNormal() - 52 - 278 + -1 + -1 - 100 - 185 + 133 + 107 + + + + + actionExit + triggered() + Drone + close() + + + -1 + -1 + + + 133 + 107 diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 007f1af..6a9052f 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -1,18 +1,25 @@ #include "drone.h" -Drone *drone; - int myport; int main(int argc, char *argv[]) { QApplication app(argc, argv); + Drone drone; + + app.setApplicationName(drone.objectName()); if (argc > 1) myport = atoi(argv[1]); - drone = new Drone; - drone->show(); - return app.exec(); + if (!drone.init()) + exit(-1); + + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone.showMinimized(); + app.exec(); + return 0; } diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( #endif -#define LOG(...) {sprintf(logStr, __VA_ARGS__); host->Log(logStr);} +#define LOG(...) {} #define MB (1024*1024) StreamInfo::StreamInfo() @@ -831,14 +831,13 @@ _found: return i; } -MyService::MyService(AbstractHost *host) +MyService::MyService() { pcap_if_t *dev; int i=0; char errbuf[PCAP_ERRBUF_SIZE]; // Init Data - this->host = host; numPorts = 0; alldevs = NULL; diff --git a/server/myservice.h b/server/myservice.h index bafe9b9..4be1bdb 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -9,7 +9,6 @@ #include "../common/protocol.pb.h" #include "../common/streambase.h" -#include "abstracthost.h" #include #include #include @@ -191,9 +190,6 @@ public: class MyService: public OstProto::OstService { - AbstractHost *host; - char logStr[1024]; - uint numPorts; /*! PortInfo::d::port_id and index into portInfo[] are same! */ @@ -203,7 +199,7 @@ class MyService: public OstProto::OstService int getStreamIndex(unsigned int portIdx,unsigned int streamId); public: - MyService(AbstractHost* host); + MyService(); virtual ~MyService(); /* Methods provided by the service */ From a1ae3e7e6c0087f8b56ff63d655771009c910477 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 26 Dec 2009 17:33:27 +0000 Subject: [PATCH 035/294] Refactoring - Major code reorganization of the server code across several classes with fewer 'friends' - New server classes - AbstractPort, PcapPort, WinPcapPort, PortManager - With this reorg classes have more focus than earlier and will be hopefully easy to extend Fixes - Ostinato client is now able to successfully reconnect and talk to the Ostinato server after a disconnect - earlier, if a method had been pending during the disconnect, the communication was not up after a reconnect; pending methods are cleaned up at disconnect now --- rpc/pbrpcchannel.cpp | 9 + rpc/pbrpccontroller.h | 2 + server/abstractport.cpp | 214 ++++++ server/abstractport.h | 82 +++ server/drone.pro | 10 +- server/drone.qrc | 5 + server/myservice.cpp | 1442 ++++++++------------------------------- server/myservice.h | 331 +++------ server/pcapextra.cpp | 116 +--- server/pcapextra.h | 34 +- server/pcapport.cpp | 407 +++++++++++ server/pcapport.h | 121 ++++ server/portmanager.cpp | 57 ++ server/portmanager.h | 26 + server/winpcapport.cpp | 144 ++++ server/winpcapport.h | 31 + 16 files changed, 1469 insertions(+), 1562 deletions(-) create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/drone.qrc create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 9d5be05..f99c00b 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -290,6 +290,15 @@ void PbRpcChannel::on_mpSocket_connected() void PbRpcChannel::on_mpSocket_disconnected() { qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + emit disconnected(); } diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index acc9520..916cb95 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -3,6 +3,8 @@ #include +class QIODevice; + class PbRpcController : public ::google::protobuf::RpcController { bool failed; diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..8cbecd9 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,214 @@ +#include "abstractport.h" + +#include +#include + +#include "../common/streambase.h" + +AbstractPort::AbstractPort(int id, const char *device) +{ + data_.mutable_port_id()->set_id(id); + data_.set_name(QString("if%1 ").arg(id).toStdString()); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + //! \todo (HIGH) port exclusive control + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = true; + linkState_ = OstProto::LinkStateUnknown; + + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +void AbstractPort::init() +{ +} + +AbstractPort::~AbstractPort() +{ +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if (streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if (streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::updatePacketList() +{ + int len; + bool isVariable; + uchar pktBuf[2000]; + long sec; + long usec; + + qDebug("In %s", __FUNCTION__); + + clearPacketList(); + //returnToQIdx = -1; + + // First sort the streams by ordinalValue + qSort(streamList_); + + sec = 0; + usec = 0; + for (int i = 0; i < streamList_.size(); i++) + { +//_restart: + if (streamList_[i]->isEnabled()) + { + long numPackets, numBursts; + long ibg, ipg; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->numBursts(); + numPackets = streamList_[i]->burstSize(); + ibg = 1000000/streamList_[i]->burstRate(); + ipg = 0; + break; + case OstProto::StreamControl::e_su_packets: + numBursts = 1; + numPackets = streamList_[i]->numPackets(); + ibg = 0; + ipg = 1000000/streamList_[i]->packetRate(); + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %ld, numPackets = %ld\n", + numBursts, numPackets); + qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + + if (streamList_[i]->isFrameVariable()) + { + isVariable = true; + } + else + { + isVariable = false; + len = streamList_[i]->frameValue(pktBuf, sizeof(pktBuf), 0); + } + + for (int j = 0; j < numBursts; j++) + { + for (int k = 0; k < numPackets; k++) + { + if (isVariable) + { + len = streamList_[i]->frameValue(pktBuf, + sizeof(pktBuf), j * numPackets + k); + } + if (len <= 0) + continue; + + usec += ipg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, sec, usec); + + appendToPacketList(sec, usec, pktBuf, len); + + } // for (numPackets) + + usec += ibg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numBursts) + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; + stats->rxBytes = stats_.rxBytes - epochStats_.rxBytes; + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = stats_.txPkts - epochStats_.txPkts; + stats->txBytes = stats_.txBytes - epochStats_.txBytes; + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..6c0bcc3 --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,82 @@ +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + virtual void init(); + + int id() { return data_.port_id().id(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + int streamCount() { return streamList_.size(); } + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual OstProto::LinkState linkState() { return linkState_; } + + virtual void clearPacketList() = 0; + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + OstProto::Port data_; + OstProto::LinkState linkState_; + + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/drone.pro b/server/drone.pro index a5cd2bb..fe06b4e 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -3,18 +3,24 @@ CONFIG += qt debug QT += network script DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "../rpc" -LIBS += -lprotobuf win32:LIBS += -lwpcap -lpacket unix:LIBS += -lpcap win32:LIBS += -L"../common/debug" -lostproto unix:LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc +LIBS += -lprotobuf POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui -SOURCES += drone_main.cpp drone.cpp +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp SOURCES += myservice.cpp SOURCES += pcapextra.cpp diff --git a/server/drone.qrc b/server/drone.qrc new file mode 100644 index 0000000..a642656 --- /dev/null +++ b/server/drone.qrc @@ -0,0 +1,5 @@ + + + icons/portgroup.png + + diff --git a/server/myservice.cpp b/server/myservice.cpp index 1fa13af..e742fe6 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,1301 +1,423 @@ +#include "myservice.h" + +#if 0 #include #include #include "qdebug.h" -#include "myservice.h" #include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" +#endif +#include "../common/streambase.h" #include "../rpc/pbrpccontroller.h" - -#if 0 -#include -#include -#endif - -#define LOG(...) {} -#define MB (1024*1024) - -StreamInfo::StreamInfo() -{ -} - -StreamInfo::~StreamInfo() -{ -} - -// -// ------------------ PortInfo -------------------- -// -PortInfo::PortInfo(uint id, pcap_if_t *dev) - : monitorRx(this), monitorTx(this), transmitter(this), capturer(this) -{ - char errbuf[PCAP_ERRBUF_SIZE]; - - this->dev = dev; - -#ifdef Q_OS_WIN32 - adapter = PacketOpenAdapter(dev->name); - if (!adapter) - qFatal("Unable to open adapter %s", dev->name); - oidData = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + sizeof(uint)); - if (oidData) - { - memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); - oidData->Length=sizeof(uint); - } - else - qFatal("failed to alloc oidData"); -#endif - - /* - * Get 2 device handles - one for rx and one for tx. If we use only - * one handle for both rx and tx anythin that we tx using the single - * handle is not received back to us - */ - devHandleRx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , - 1000 /*ms*/, errbuf); - if (devHandleRx == NULL) - { - qDebug("Error opening port %s: %s\n", - dev->name, pcap_geterr(devHandleRx)); - } - -#if 0 - if (pcap_setdirection(devHandleRx, PCAP_D_IN)<0) - { - qDebug("[%s] Error setting direction inbound only\n", dev->name); - } -#endif - - /* By default, put the interface in statistics mode */ - if (pcap_setmode(devHandleRx, MODE_STAT)<0) - { - qDebug("Error setting statistics mode.\n"); - } - - devHandleTx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS , - 1000 /*ms*/, errbuf); - if (devHandleTx == NULL) - { - qDebug("Error opening port %s: %s\n", - dev->name, pcap_geterr(devHandleTx)); - } - -#if 0 - if (pcap_setdirection(devHandleTx, PCAP_D_OUT)<0) - { - qDebug("[%s] Error setting direction outbound only\n", dev->name); - } -#endif - - /* By default, put the interface in statistics mode */ - if (pcap_setmode(devHandleTx, MODE_STAT)<0) - { - qDebug("Error setting statistics mode.\n"); - } - - d.mutable_port_id()->set_id(id); - -#ifdef Q_OS_WIN32 - d.set_name(QString("if%1 ").arg(id).toAscii().constData()); -#else - if (dev->name) - d.set_name(dev->name); - else - d.set_name(QString("if%1 ").arg(id).toAscii().constData()); -#endif - d.set_name(d.name()+"{"+ - pcap_datalink_val_to_name(pcap_datalink(devHandleRx))+"}"); - - if (dev->description) - d.set_description(dev->description); - d.set_is_enabled(true); //! \todo (LOW) admin enable/disable of port - d.set_is_exclusive_control(false); //! \todo (HIGH) port exclusive control - - memset((void*) &stats, 0, sizeof(stats)); - resetStats(); - - linkState = OstProto::LinkStateUnknown; - - // We'll create sendqueue later when required - sendQueueList.clear(); - returnToQIdx = -1; - pcapExtra.txPkts = 0; - pcapExtra.txBytes = 0; - isSendQueueDirty=true; - - // Start the monitor thread - monitorRx.start(); - monitorTx.start(); -} - -void PortInfo::updateLinkState() -{ -#ifdef Q_OS_WIN32 - OstProto::LinkState newLinkState - = OstProto::LinkStateUnknown; - - memset(oidData, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); - oidData->Oid = OID_GEN_MEDIA_CONNECT_STATUS; - oidData->Length = sizeof(uint); - if (PacketRequest(adapter, 0, oidData)) - { - uint state; - - if (oidData->Length == sizeof(state)) - { - memcpy((void*)&state, (void*)oidData->Data, oidData->Length); - if (state == 0) - newLinkState = OstProto::LinkStateUp; - else if (state == 1) - newLinkState = OstProto::LinkStateDown; - } - } - - linkState = newLinkState; -#elif defined(Q_OS_LINUX) - //! \todo (HI) implement link state for linux - get from /proc maybe? -#endif -} - -void PortInfo::update() -{ - int len; - bool isVariable; - uchar pktBuf[2000]; - pcap_pkthdr pktHdr; - ost_pcap_send_queue sendQ; - - qDebug("In %s", __FUNCTION__); - - if (sendQueueList.size()) - { - foreach(sendQ, sendQueueList) - pcap_sendqueue_destroy(sendQ.sendQueue); - } - sendQueueList.clear(); - returnToQIdx = -1; - - //! \todo (LOW): calculate sendqueue size - sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); - sendQ.sendQueueCumLen.clear(); - - // First sort the streams by ordinalValue - qSort(streamList); - - pktHdr.ts.tv_sec = 0; - pktHdr.ts.tv_usec = 0; - for (int i = 0; i < streamList.size(); i++) - { -//_restart: - if (streamList[i]->isEnabled()) - { - long numPackets, numBursts; - long ibg, ipg; - - switch (streamList[i]->sendUnit()) - { - case OstProto::StreamControl::e_su_bursts: - numBursts = streamList[i]->numBursts(); - numPackets = streamList[i]->burstSize(); - ibg = 1000000/streamList[i]->burstRate(); - ipg = 0; - break; - case OstProto::StreamControl::e_su_packets: - numBursts = 1; - numPackets = streamList[i]->numPackets(); - ibg = 0; - ipg = 1000000/streamList[i]->packetRate(); - break; - default: - qWarning("Unhandled stream control unit %d", - streamList[i]->sendUnit()); - continue; - } - qDebug("numBursts = %ld, numPackets = %ld\n", - numBursts, numPackets); - qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); - - if (streamList[i]->isFrameVariable()) - { - isVariable = true; - } - else - { - isVariable = false; - len = streamList[i]->frameValue(pktBuf, sizeof(pktBuf), 0); - } - - for (int j = 0; j < numBursts; j++) - { - for (int k = 0; k < numPackets; k++) - { - if (isVariable) - { - len = streamList[i]->frameValue(pktBuf, sizeof(pktBuf), - j * numPackets + k); - } - if (len > 0) - { - pktHdr.caplen = pktHdr.len = len; - pktHdr.ts.tv_usec += ipg; - if (pktHdr.ts.tv_usec > 1000000) - { - pktHdr.ts.tv_sec++; - pktHdr.ts.tv_usec -= 1000000; - } - - // Not enough space? Alloc another one! - if ((sendQ.sendQueue->len + len + sizeof(pcap_pkthdr)) - > sendQ.sendQueue->maxlen) - { - sendQueueList.append(sendQ); - - //! \todo (LOW): calculate sendqueue size - sendQ.sendQueue = pcap_sendqueue_alloc(1*MB); - sendQ.sendQueueCumLen.clear(); - -#if 0 - pktHdr.ts.tv_sec = 0; - pktHdr.ts.tv_usec = 0; -#endif - } - - qDebug("q(%d, %d, %d) sec = %lu usec = %lu", - i, j, k, pktHdr.ts.tv_sec, pktHdr.ts.tv_usec); - - if (-1 == pcap_sendqueue_queue(sendQ.sendQueue, &pktHdr, - (u_char*) pktBuf)) - { - qDebug("[port %d] sendqueue_queue() failed for " - "streamidx %d\n", id(), i); - } - else - sendQ.sendQueueCumLen.append(sendQ.sendQueue->len); - } - } // for (numPackets) - pktHdr.ts.tv_usec += ibg; - if (pktHdr.ts.tv_usec > 1000000) - { - pktHdr.ts.tv_sec++; - pktHdr.ts.tv_usec -= 1000000; - } - } // for (numBursts) - - switch(streamList[i]->nextWhat()) - { - case ::OstProto::StreamControl::e_nw_stop: - goto _stop_no_more_pkts; - - case ::OstProto::StreamControl::e_nw_goto_id: - /*! \todo (MED): define and use - streamList[i].d.control().goto_stream_id(); */ - - /*! \todo (MED): assumes goto Id is less than current!!!! - To support goto to any id, do - if goto_id > curr_id then - i = goto_id; - goto restart; - else - returnToQIdx = 0; - */ - - returnToQIdx=0; - goto _stop_no_more_pkts; - - case ::OstProto::StreamControl::e_nw_goto_next: - break; - - default: - qFatal("---------- %s: Unhandled case (%d) -----------", - __FUNCTION__, streamList[i]->nextWhat() ); - break; - } - - } // if (stream is enabled) - } // for (numStreams) - -_stop_no_more_pkts: - // The last alloc'ed sendQ appended here - sendQueueList.append(sendQ); - - isSendQueueDirty = false; -} - -void PortInfo::startTransmit() -{ - transmitter.start(); -} - -void PortInfo::stopTransmit() -{ - transmitter.stop(); -} - -void PortInfo::startCapture() -{ - capturer.start(); -} - -void PortInfo::stopCapture() -{ - capturer.stop(); -} - -QFile* PortInfo::captureFile() -{ - return capturer.captureFile(); -} - -void PortInfo::resetStats() -{ - memcpy((void*) &epochStats, (void*) &stats, sizeof(stats)); -} - -// -// ------------------ PortMonitor ------------------- -// - -PortInfo::PortMonitorRx::PortMonitorRx(PortInfo *port) -{ - this->port = port; -#ifdef Q_OS_WIN32 - { - int sz = sizeof(PACKET_OID_DATA) + sizeof(quint64) + 4; - //oidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, - //sizeof(PACKET_OID_DATA) + sizeof(quint64) - 1); - oidData = (PPACKET_OID_DATA) malloc(sz); - if (oidData) - { - memset(oidData, 0, sz); - oidData->Length=sizeof(quint64); - } - else - qFatal("failed to alloc oidData"); - } -#endif -} - -PortInfo::PortMonitorTx::PortMonitorTx(PortInfo *port) -{ - this->port = port; -#ifdef Q_OS_WIN32 - { - int sz = sizeof(PACKET_OID_DATA) + sizeof(quint64) + 4; - //oidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, - //sizeof(PACKET_OID_DATA) + sizeof(quint64) - 1); - oidData = (PPACKET_OID_DATA) malloc(sz); - if (oidData) - { - memset(oidData, 0, sz); - oidData->Length=sizeof(quint64); - } - else - qFatal("failed to alloc oidData"); - } -#endif -} - -#ifdef Q_OS_WIN32 -void PortInfo::PortMonitorRx::callbackRx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data) -{ - // This is the WinPcap Callback - which is a 'stats mode' callback - - uint usec; - PortInfo *port = (PortInfo*) state; - - quint64 pkts; - quint64 bytes; - - // Update RxStats and RxRates using PCAP data - pkts = *((quint64*)(pkt_data + 0)); - bytes = *((quint64*)(pkt_data + 8)); - -#if 0 - if (port->id() == 2) - qDebug("# %llu", pkts); -#endif - - // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it - bytes -= pkts * ETH_FRAME_HDR_SIZE; - - usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTsRx.tv_usec); - port->stats.rxPps = (pkts * 1000000) / usec; - port->stats.rxBps = (bytes * 1000000) / usec; - - port->stats.rxPkts += pkts; - port->stats.rxBytes += bytes; - - // Store curr timestamp as last timestamp - port->lastTsRx.tv_sec = header->ts.tv_sec; - port->lastTsRx.tv_usec = header->ts.tv_usec; - -#if 0 - for (int i=0; i < 16; i++) - { - qDebug("%02x ", pkt_data[i]); - } - qDebug("{%d: %llu, %llu}\n", port->id(), - pkts, bytes); - qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts); - qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes); -#endif - - // Retreive NIC stats -#if 0 - port->monitorRx.oidData->Oid = OID_GEN_RCV_OK; - if (PacketRequest(port->devHandleRx->adapter, 0, port->monitorRx.oidData)) - { - if (port->monitorRx.oidData->Length <= sizeof(port->stats.rxPktsNic)) - memcpy((void*)&port->stats.rxPktsNic, - (void*)port->monitorRx.oidData->Data, - port->monitorRx.oidData->Length); - } -#endif -} -void PortInfo::PortMonitorTx::callbackTx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data) -{ - // This is the WinPcap Callback - which is a 'stats mode' callback - - uint usec; - PortInfo *port = (PortInfo*) state; - - quint64 pkts; - quint64 bytes; - - -#if 0 - // Update RxStats and RxRates using PCAP data - pkts = *((quint64*)(pkt_data + 0)); - bytes = *((quint64*)(pkt_data + 8)); - -#if 0 - if (port->id() == 2) - qDebug("@ %llu", pkts); -#endif - - // Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it - bytes -= pkts * ETH_FRAME_HDR_SIZE; - - usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTsTx.tv_usec); - port->stats.txPps = (pkts * 1000000) / usec; - port->stats.txBps = (bytes * 1000000) / usec; - - port->stats.txPkts += pkts; - port->stats.txBytes += bytes; -#endif - - // Since WinPCAP (due to NDIS limitation) cannot distinguish between - // rx/tx packets, pcap stats are not of much use - for the tx stats - // update from PcapExtra - - pkts = port->pcapExtra.txPkts - port->stats.txPkts; - bytes = port->pcapExtra.txBytes - port->stats.txBytes; - - // Use the pcap timestamp for rate calculation though - usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTsTx.tv_usec); - port->stats.txPps = (pkts * 1000000) / usec; - port->stats.txBps = (bytes * 1000000) / usec; - - port->stats.txPkts = port->pcapExtra.txPkts; - port->stats.txBytes = port->pcapExtra.txBytes; - - // Store curr timestamp as last timestamp - port->lastTsTx.tv_sec = header->ts.tv_sec; - port->lastTsTx.tv_usec = header->ts.tv_usec; - -#if 0 - for (int i=0; i < 16; i++) - { - qDebug("%02x ", pkt_data[i]); - } - qDebug("{%d: %llu, %llu}\n", port->id(), - pkts, bytes); - qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts); - qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes); -#endif - - // Retreive NIC stats -#if 0 - port->monitorTx.oidData->Oid = OID_GEN_XMIT_OK; - if (PacketRequest(port->devHandleTx->adapter, 0, port->monitorTx.oidData)) - { - if (port->monitorTx.oidData->Length <= sizeof(port->stats.txPktsNic)) - memcpy((void*)&port->stats.txPktsNic, - (void*)port->monitorTx.oidData->Data, - port->monitorTx.oidData->Length); - } -#endif -} -#else -void PortInfo::PortMonitorRx::callbackRx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data) -{ - // This is the LibPcap Callback - which is a 'capture mode' callback - // This callback is called once for EVERY packet - - uint usec; - PortInfo *port = (PortInfo*) state; - - quint64 pkts; - quint64 bytes; - - // Update RxStats and RxRates using PCAP data - usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTsRx.tv_usec); - //! \todo support Rx Pkt/Bit rate on Linux (libpcap callback) -#if 0 - port->stats.rxPps = (pkts * 1000000) / usec; - port->stats.rxBps = (bytes * 1000000) / usec; -#endif - - // Note: For a 'capture callback' PCAP reported bytes DOES NOT include - // ETH_FRAME_HDR_SIZE - so don't adjust for it - port->stats.rxPkts++; - port->stats.rxBytes += header->len; - - // Store curr timestamp as last timestamp - port->lastTsRx.tv_sec = header->ts.tv_sec; - port->lastTsRx.tv_usec = header->ts.tv_usec; -} - -void PortInfo::PortMonitorTx::callbackTx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data) -{ - // This is the LibPcap Callback - which is a 'capture mode' callback - // This callback is called once for EVERY packet - - uint usec; - PortInfo *port = (PortInfo*) state; - - quint64 pkts; - quint64 bytes; - - // Update TxStats and TxRates using PCAP data - usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 + - (header->ts.tv_usec - port->lastTsTx.tv_usec); - //! \todo support Tx Pkt/Bit rate on Linux (libpcap callback) -#if 0 - port->stats.txPps = (pkts * 1000000) / usec; - port->stats.txBps = (bytes * 1000000) / usec; -#endif - - // Note: For a 'capture callback' PCAP reported bytes DOES NOT include - // ETH_FRAME_HDR_SIZE - so don't adjust for it - - port->stats.txPkts++; - port->stats.txBytes += header->len; - - // Store curr timestamp as last timestamp - port->lastTsTx.tv_sec = header->ts.tv_sec; - port->lastTsTx.tv_usec = header->ts.tv_usec; -} -#endif -void PortInfo::PortMonitorRx::run() -{ - int ret; - - qDebug("before pcap_loop rx \n"); -#if 1 - /* Start the main loop */ - ret = pcap_loop(port->devHandleRx, -1, - &PortInfo::PortMonitorRx::callbackRx, (u_char*) port); - - switch(ret) - { - case 0: - qDebug("Unexpected return from pcap_loop()\n"); - break; - case -1: - qDebug("Unsolicited (error) return from pcap_loop()\n"); - break; - case -2: - qDebug("Solicited return from pcap_loop()\n"); - break; - default: - qDebug("Unknown return value from pcap_loop()\n"); - } -#else - while (1) - { - /* Start the main loop */ - ret = pcap_dispatch(port->devHandleRx, -1, - &PortInfo::PortMonitorRx::callbackRx, (u_char*) port); - - switch(ret) - { - case -1: - qDebug("Unsolicited (error) return from pcap_loop() %s\n", - pcap_geterr(port->devHandleRx)); - break; - case -2: - qDebug("Solicited return from pcap_loop()\n"); - break; - default: - //qDebug("%d pkts rcvd\n", ret); - break; - } - } -#endif -} - -void PortInfo::PortMonitorTx::run() -{ - int ret; - - qDebug("before pcap_loopTx\n"); -#if 1 - /* Start the main loop */ - ret = pcap_loop(port->devHandleTx, -1, - &PortInfo::PortMonitorTx::callbackTx, (u_char*) port); - - switch(ret) - { - case 0: - qDebug("Unexpected return from pcap_loop()\n"); - break; - case -1: - qDebug("Unsolicited (error) return from pcap_loop()\n"); - break; - case -2: - qDebug("Solicited return from pcap_loop()\n"); - break; - default: - qDebug("Unknown return value from pcap_loop()\n"); - } -#else - while (1) - { - /* Start the main loop */ - ret = pcap_dispatch(port->devHandleTx, -1, - &PortInfo::PortMonitorTx::callbackTx, (u_char*) port); - - switch(ret) - { - case -1: - qDebug("Unsolicited (error) return from pcap_loop() %s\n", - pcap_geterr(port->devHandleTx)); - break; - case -2: - qDebug("Solicited return from pcap_loop()\n"); - break; - default: - //qDebug("%d pkts rcvd\n", ret); - break; - } - } -#endif -} - -/*--------------- PortTransmitter ---------------*/ - -PortInfo::PortTransmitter::PortTransmitter(PortInfo *port) -{ - this->port = port; -} - -void PortInfo::PortTransmitter::run() -{ - //! \todo (MED) Stream Mode - continuous: define before implement - - // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 - // 'coz of 2 reasons - there's no way of stopping it before all packets - // in the sendQueue are sent out and secondly, stats are available only - // when all packets have been sent - no periodic updates - // - // NOTE2: Transmit on the Rx Handle so that we can receive it back - // on the Tx Handle to do stats - // - // NOTE3: Update pcapExtra counters - port TxStats will be updated in the - // 'stats callback' function so that both Rx and Tx stats are updated - // together - - m_stop = 0; - ost_pcap_sendqueue_list_transmit(port->devHandleRx, port->sendQueueList, - port->returnToQIdx, true, &m_stop, - &port->pcapExtra.txPkts, &port->pcapExtra.txBytes, - QThread::usleep); - m_stop = 0; -} - -void PortInfo::PortTransmitter::stop() -{ - m_stop = 1; -} - -/*--------------- PortCapture ---------------*/ - -PortInfo::PortCapture::PortCapture(PortInfo *port) -{ - this->port = port; - capHandle = NULL; - dumpHandle = NULL; -} - -PortInfo::PortCapture::~PortCapture() -{ -} - -void PortInfo::PortCapture::run() -{ - int ret; - char errbuf[PCAP_ERRBUF_SIZE]; - - capHandle = pcap_open_live(port->dev->name, 65535, - PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); - if (capHandle == NULL) - { - qDebug("Error opening port %s: %s\n", - port->dev->name, pcap_geterr(capHandle)); - return; - } - - if (!capFile.isOpen()) - { - if (!capFile.open()) - qFatal("Unable to open temp cap file"); - } - - qDebug("cap file = %s", capFile.fileName().toAscii().constData()); - dumpHandle = pcap_dump_open(capHandle, - capFile.fileName().toAscii().constData()); - - m_stop = 0; - while (m_stop == 0) - { - struct pcap_pkthdr *hdr; - const uchar *data; - - ret = pcap_next_ex(capHandle, &hdr, &data); - switch (ret) - { - case 1: - pcap_dump((uchar*) dumpHandle, hdr, data); - case 0: - continue; - case -1: - qWarning("%s: error reading packet (%d): %s", - __PRETTY_FUNCTION__, ret, pcap_geterr(capHandle)); - break; - case -2: - default: - qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); - } - } - m_stop = 0; - pcap_dump_close(dumpHandle); - pcap_close(capHandle); - dumpHandle = NULL; - capHandle = NULL; -} - -void PortInfo::PortCapture::stop() -{ - m_stop = 1; -} - -QFile* PortInfo::PortCapture::captureFile() -{ - return &capFile; -} - - -/*--------------- MyService ---------------*/ - -int MyService::getStreamIndex(unsigned int portIdx, - unsigned int streamId) -{ - int i; - - // note: index and id are interchageable for port but not for stream - - Q_ASSERT(portIdx < numPorts); - - for (i = 0; i < portInfo[portIdx]->streamList.size(); i++) - { - if (streamId == portInfo[portIdx]->streamList.at(i)->mStreamId.id()) - goto _found; - } - - qDebug("%s: stream id %d not found", __PRETTY_FUNCTION__, streamId); - return -1; - -_found: - return i; -} +#include "portmanager.h" MyService::MyService() { - pcap_if_t *dev; - int i=0; - char errbuf[PCAP_ERRBUF_SIZE]; + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); - // Init Data - numPorts = 0; - alldevs = NULL; - - LOG("Retrieving the device list from the local machine\n"); - if (pcap_findalldevs(&alldevs, errbuf) == -1) - { - LOG("Error in pcap_findalldevs_ex: %s\n", errbuf); - goto _fail; - } - - portInfo.clear(); - /* Count, Populate and Print the list */ - for(i=0, dev=alldevs; dev!=NULL; i++, dev=dev->next) - { - portInfo.append(new PortInfo(i, dev)); - numPorts++; - -#if 1 - LOG("%d. %s", i, dev->name); - if (dev->description) - { - LOG(" (%s)\n", dev->description); - } -#endif - } - - if (i == 0) - { - LOG("\nNo interfaces found! Make sure WinPcap is installed.\n"); - goto _fail; - } - -_fail: - return; + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); } MyService::~MyService() { - pcap_freealldevs(alldevs); } -void MyService::getPortIdList( - ::google::protobuf::RpcController* controller, - const ::OstProto::Void* request, - ::OstProto::PortIdList* response, - ::google::protobuf::Closure* done) +void MyService::getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - for (uint i = 0; i < numPorts; i++) - { - ::OstProto::PortId *p; + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; - p = response->add_port_id(); - p->set_id(portInfo[i]->d.port_id().id()); - } + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } - done->Run(); + done->Run(); } void MyService::getPortConfig(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::PortConfigList* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - unsigned int idx; + for (int i = 0; i < request->port_id_size(); i++) + { + int id; - idx = request->port_id(i).id(); - if (idx < numPorts) - { - OstProto::Port *p; + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; - p = response->add_port(); - p->CopyFrom(portInfo[idx]->d); - } - } + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } - done->Run(); + done->Run(); } void MyService::getStreamIdList(::google::protobuf::RpcController* controller, -const ::OstProto::PortId* request, -::OstProto::StreamIdList* response, -::google::protobuf::Closure* done) + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) { - unsigned int portIdx; + int portId; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - portIdx = request->id(); - if (portIdx >= numPorts) - { - qDebug("%s: Invalid port id %d", __PRETTY_FUNCTION__, portIdx); - controller->SetFailed("Invalid Port Id"); - goto _exit; //! \todo (LOW): Partial status of RPC - } + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; - response->mutable_port_id()->set_id(portIdx); - for (int j = 0; j < portInfo[portIdx]->streamList.size(); j++) - { - OstProto::StreamId *s; + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; - s = response->add_stream_id(); - s->CopyFrom(portInfo[portIdx]->streamList[j]->mStreamId); - } + s = response->add_stream_id(); + s->set_id(portInfo[portId]->stream(i)->id()); + } + done->Run(); + return; -_exit: - done->Run(); +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); } void MyService::getStreamConfig(::google::protobuf::RpcController* controller, -const ::OstProto::StreamIdList* request, -::OstProto::StreamConfigList* response, -::google::protobuf::Closure* done) + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) { - unsigned int portIdx; + int portId; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - portIdx = request->port_id().id(); - if (portIdx >= numPorts) - { - controller->SetFailed("invalid portid"); - goto _exit; - } + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; - response->mutable_port_id()->set_id(portIdx); - for (int i = 0; i < request->stream_id_size(); i++) - { - int streamIndex; - OstProto::Stream *s; + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; - streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); - if (streamIndex < 0) - continue; //! \todo(LOW): Partial status of RPC + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC - s = response->add_stream(); + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; - portInfo[portIdx]->streamList[streamIndex]->protoDataCopyInto(*s); - } - -_exit: - done->Run(); +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); } void MyService::addStream(::google::protobuf::RpcController* controller, -const ::OstProto::StreamIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - unsigned int portIdx; + int portId; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - portIdx = request->port_id().id(); - if (portIdx >= numPorts) - { - controller->SetFailed("invalid portid"); - goto _exit; - } + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; - for (int i = 0; i < request->stream_id_size(); i++) - { - int streamIndex; - StreamInfo *s = new StreamInfo; + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; - // If stream with same id as in request exists already ==> error!! - streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); - if (streamIndex >= 0) - continue; //! \todo (LOW): Partial status of RPC + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC - // Append a new "default" stream - actual contents of the new stream is - // expected in a subsequent "modifyStream" request - set the stream id - // now itself however!!! - s->mStreamId.CopyFrom(request->stream_id(i)); - portInfo[portIdx]->streamList.append(s); + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); - //! \todo (LOW): fill-in response "Ack"???? - } - portInfo[portIdx]->setDirty(true); -_exit: - done->Run(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); } void MyService::deleteStream(::google::protobuf::RpcController* controller, -const ::OstProto::StreamIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - unsigned int portIdx; + int portId; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - portIdx = request->port_id().id(); - if (portIdx >= numPorts) - { - controller->SetFailed("invalid portid"); - goto _exit; - } + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; - for (int i = 0; i < request->stream_id_size(); i++) - { - int streamIndex; - StreamInfo s; + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); - streamIndex = getStreamIndex(portIdx, request->stream_id(i).id()); - if (streamIndex < 0) - continue; //! \todo (LOW): Partial status of RPC + //! \todo (LOW): fill-in response "Ack"???? - delete portInfo[portIdx]->streamList.takeAt(streamIndex); + done->Run(); + return; - //! \todo (LOW): fill-in response "Ack"???? - } - portInfo[portIdx]->setDirty(true); -_exit: - done->Run(); +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); } void MyService::modifyStream(::google::protobuf::RpcController* controller, -const ::OstProto::StreamConfigList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - unsigned int portIdx; + int portId; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - portIdx = request->port_id().id(); - if (portIdx >= numPorts) - { - controller->SetFailed("invalid portid"); - goto _exit; - } + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; - for (int i = 0; i < request->stream_size(); i++) - { - int streamIndex; + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; - streamIndex = getStreamIndex(portIdx, - request->stream(i).stream_id().id()); - if (streamIndex < 0) - continue; //! \todo (LOW): Partial status of RPC + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } - portInfo[portIdx]->streamList[streamIndex]->protoDataCopyFrom( - request->stream(i)); + //! \todo(LOW): fill-in response "Ack"???? - //! \todo(LOW): fill-in response "Ack"???? - } - portInfo[portIdx]->setDirty(true); -_exit: - done->Run(); + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); } void MyService::startTx(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - // If any of the ports in the request are dirty, first update them - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? - if (portInfo[portIdx]->isDirty()) - portInfo[portIdx]->update(); - } + portInfo[portId]->startTransmit(); + } - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + //! \todo (LOW): fill-in response "Ack"???? - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? - - portInfo[portIdx]->startTransmit(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); + done->Run(); } void MyService::stopTx(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? - portInfo[portIdx]->stopTransmit(); - } - //! \todo (LOW): fill-in response "Ack"???? - done->Run(); + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); } void MyService::startCapture(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? - portInfo[portIdx]->startCapture(); - } + portInfo[portId]->startCapture(); + } - done->Run(); + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); } void MyService::stopCapture(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? - portInfo[portIdx]->stopCapture(); - } + portInfo[portId]->stopCapture(); + } - done->Run(); + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); } void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, -const ::OstProto::PortId* request, -::OstProto::CaptureBuffer* response, -::google::protobuf::Closure* done) + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done) { - uint portIdx; - qDebug("In %s", __PRETTY_FUNCTION__); + int portId; - portIdx = request->id(); - if (portIdx >= numPorts) - { - controller->SetFailed("invalid portid"); - goto _exit; - } + qDebug("In %s", __PRETTY_FUNCTION__); - portInfo[portIdx]->stopCapture(); - static_cast(controller)->setBinaryBlob( - portInfo[portIdx]->captureFile()); + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; -_exit: - done->Run(); + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); } void MyService::getStats(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::PortStatsList* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) { - //qDebug("In %s", __PRETTY_FUNCTION__); + //qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - uint portidx; - ::OstProto::PortStats *s; - OstProto::PortState *st; + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; - portidx = request->port_id(i).id(); - if (portidx >= numPorts) - continue; //! \todo(LOW): partial rpc? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? - s = response->add_port_stats(); - s->mutable_port_id()->set_id(request->port_id(i).id()); + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); #if 0 - if (portidx == 2) - { - qDebug("<%llu", portInfo[portidx]->epochStats.rxPkts); - qDebug(">%llu", portInfo[portidx]->stats.rxPkts); - } + if (portId == 2) + qDebug(">%llu", stats.rxPkts); #endif - portInfo[portidx]->updateLinkState(); + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); - st = s->mutable_state(); - st->set_link_state(portInfo[portidx]->linkState); - st->set_is_transmit_on(portInfo[portidx]->transmitter.isRunning()); - st->set_is_capture_on(portInfo[portidx]->capturer.isRunning()); + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + } - s->set_rx_pkts(portInfo[portidx]->stats.rxPkts - - portInfo[portidx]->epochStats.rxPkts); - s->set_rx_bytes(portInfo[portidx]->stats.rxBytes - - portInfo[portidx]->epochStats.rxBytes); - s->set_rx_pkts_nic(portInfo[portidx]->stats.rxPktsNic - - portInfo[portidx]->epochStats.rxPktsNic); - s->set_rx_bytes_nic(portInfo[portidx]->stats.rxBytesNic - - portInfo[portidx]->epochStats.rxBytesNic); - s->set_rx_pps(portInfo[portidx]->stats.rxPps); - s->set_rx_bps(portInfo[portidx]->stats.rxBps); - - s->set_tx_pkts(portInfo[portidx]->stats.txPkts - - portInfo[portidx]->epochStats.txPkts); - s->set_tx_bytes(portInfo[portidx]->stats.txBytes - - portInfo[portidx]->epochStats.txBytes); - s->set_tx_pkts_nic(portInfo[portidx]->stats.txPktsNic - - portInfo[portidx]->epochStats.txPktsNic); - s->set_tx_bytes_nic(portInfo[portidx]->stats.txBytesNic - - portInfo[portidx]->epochStats.txBytesNic); - s->set_tx_pps(portInfo[portidx]->stats.txPps); - s->set_tx_bps(portInfo[portidx]->stats.txBps); - } - - done->Run(); + done->Run(); } void MyService::clearStats(::google::protobuf::RpcController* controller, -const ::OstProto::PortIdList* request, -::OstProto::Ack* response, -::google::protobuf::Closure* done) + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) { - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - uint portIdx; + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; - portIdx = request->port_id(i).id(); - if (portIdx >= numPorts) - continue; //! \todo (LOW): partial RPC? + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? - portInfo[portIdx]->resetStats(); - } - //! \todo (LOW): fill-in response "Ack"???? + portInfo[portId]->resetStats(); + } - done->Run(); + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); } +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/myservice.h b/server/myservice.h index 4be1bdb..5d434ee 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -1,264 +1,85 @@ -#ifndef _MY_SERVICE_H -#define _MY_SERVICE_H - - -#if 0 -#include -#include -#endif - -#include "../common/protocol.pb.h" -#include "../common/streambase.h" -#include -#include -#include -#include -#include - -#include "../rpc/pbhelper.h" -#include "pcapextra.h" +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H -#ifdef Q_OS_WIN32 -#include -#endif +#include -#ifdef Q_OS_WIN32 -#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 -#endif - -#define MAX_PKT_HDR_SIZE 1536 -#define MAX_STREAM_NAME_SIZE 64 - -//! 7 byte Preamble + 1 byte SFD + 4 byte FCS -#define ETH_FRAME_HDR_SIZE 12 - +#include "../common/protocol.pb.h" -class MyService; - -class StreamInfo : public StreamBase -{ - friend class MyService; - friend class PortInfo; - - OstProto::StreamId mStreamId; - +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ public: - StreamInfo(); - ~StreamInfo(); -}; - - -class PortInfo -{ - friend class MyService; - - class PortMonitorRx: public QThread - { - friend class PortInfo; - - PortInfo *port; -#ifdef Q_OS_WIN32 - PPACKET_OID_DATA oidData; -#endif - public: - PortMonitorRx(PortInfo *port); - static void callbackRx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data); - void run(); - }; + MyService(); + virtual ~MyService(); - class PortMonitorTx: public QThread - { - friend class PortInfo; - - PortInfo *port; -#ifdef Q_OS_WIN32 - PPACKET_OID_DATA oidData; -#endif - public: - PortMonitorTx(PortInfo *port); - static void callbackTx(u_char *state, - const struct pcap_pkthdr *header, const u_char *pkt_data); - void run(); - }; + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); - class PortTransmitter: public QThread - { - friend class PortInfo; +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; - PortInfo *port; - int m_stop; +}; - public: - PortTransmitter(PortInfo *port); - void run(); - void stop(); - }; +#endif - class PortCapture: public QThread - { - friend class PortInfo; - - PortInfo *port; - int m_stop; - pcap_t *capHandle; - pcap_dumper_t *dumpHandle; - QTemporaryFile capFile; - - public: - PortCapture(PortInfo *port); - ~PortCapture(); - void run(); - void stop(); - QFile* captureFile(); - }; - -#ifdef Q_OS_WIN32 - LPADAPTER adapter; - PPACKET_OID_DATA oidData; -#endif - - OstProto::Port d; - OstProto::LinkState linkState; - - struct PortStats - { - quint64 rxPkts; - quint64 rxBytes; - quint64 rxPktsNic; - quint64 rxBytesNic; - quint64 rxPps; - quint64 rxBps; - - quint64 txPkts; - quint64 txBytes; - quint64 txPktsNic; - quint64 txBytesNic; - quint64 txPps; - quint64 txBps; - }; - - //! \todo Need lock for stats access/update - - - //! Stuff we need to maintain since PCAP doesn't as of now. As and when - // PCAP supports it, we'll remove from here - struct PcapExtra - { - - //! PCAP doesn't do any tx stats - quint64 txPkts; - quint64 txBytes; - - }; - - pcap_if_t *dev; - pcap_t *devHandleRx; - pcap_t *devHandleTx; - QList sendQueueList; - int returnToQIdx; // FIXME(MED): combine with sendQList - bool isSendQueueDirty; - PcapExtra pcapExtra; - PortMonitorRx monitorRx; - PortMonitorTx monitorTx; - PortTransmitter transmitter; - PortCapture capturer; - - struct PortStats epochStats; - struct PortStats stats; - struct timeval lastTsRx; //! used for Rate Stats calculations - struct timeval lastTsTx; //! used for Rate Stats calculations - - /*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */ - QList streamList; - -public: - PortInfo(uint id, pcap_if_t *dev); - uint id() { return d.port_id().id(); } - void updateLinkState(); - bool isDirty() { return isSendQueueDirty; } - void setDirty(bool dirty) { isSendQueueDirty = dirty; } - void update(); - void startTransmit(); - void stopTransmit(); - void startCapture(); - void stopCapture(); - QFile* captureFile(); - void resetStats(); -}; - - -class MyService: public OstProto::OstService -{ - uint numPorts; - - /*! PortInfo::d::port_id and index into portInfo[] are same! */ - QList portInfo; - pcap_if_t *alldevs; - - int getStreamIndex(unsigned int portIdx,unsigned int streamId); - -public: - MyService(); - virtual ~MyService(); - - /* Methods provided by the service */ - virtual void getPortIdList(::google::protobuf::RpcController* controller, - const ::OstProto::Void* request, - ::OstProto::PortIdList* response, - ::google::protobuf::Closure* done); - virtual void getPortConfig(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortConfigList* response, - ::google::protobuf::Closure* done); - virtual void getStreamIdList(::google::protobuf::RpcController* controller, - const ::OstProto::PortId* request, - ::OstProto::StreamIdList* response, - ::google::protobuf::Closure* done); - virtual void getStreamConfig(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::StreamConfigList* response, - ::google::protobuf::Closure* done); - virtual void addStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void deleteStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void modifyStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamConfigList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void startTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void stopTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void startCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void stopCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); - virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, - const ::OstProto::PortId* request, - ::OstProto::CaptureBuffer* response, - ::google::protobuf::Closure* done); - virtual void getStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortStatsList* response, - ::google::protobuf::Closure* done); - virtual void clearStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done); -}; - -#endif +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 197e83b..8274d48 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -1,16 +1,11 @@ +#include "pcapextra.h" + #include // memcpy() #include // malloc(), free() -#include "pcapextra.h" /* NOTE: All code borrowed from WinPcap */ #ifndef Q_OS_WIN32 -int pcap_setmode(pcap_t *p, int mode) -{ - // no STAT mode in libpcap, so just return 0 to indicate success - return 0; -} - pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) { pcap_send_queue *tqueue; @@ -61,111 +56,4 @@ int pcap_sendqueue_queue (pcap_send_queue *queue, } #endif -u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int returnToQIdx, int sync, - int *p_stop, quint64* p_pkts, quint64* p_bytes, - void (*pf_usleep)(ulong)) -{ - uint i, ret = 0; - ost_pcap_send_queue sq; - - for(i = 0; i < sendQueueList.size(); i++) - { -_restart: - sq = sendQueueList.at(i); - ret += ost_pcap_sendqueue_transmit(p, sq.sendQueue, sync, - p_stop, p_pkts, p_bytes, pf_usleep); - - if (*p_stop) - return ret; - - //! \todo (HIGH): Timing between subsequent sendQueues - } - - if (returnToQIdx >= 0) - { - i = returnToQIdx; - - //! \todo (HIGH) 1s fixed; Change this to ipg of last stream - (*pf_usleep)(1000000); - goto _restart; - } - - return ret; -} - -u_int ost_pcap_sendqueue_transmit(pcap_t *p, - pcap_send_queue *queue, int sync, - int *p_stop, quint64* p_pkts, quint64* p_bytes, - void (*pf_usleep)(ulong)) -{ - char* PacketBuff = queue->buffer; - int Size = queue->len; - - struct pcap_pkthdr *winpcap_hdr; - struct timeval ts; - char* EndOfUserBuff = (char *)PacketBuff + Size; - int ret; - - // Start from the first packet - winpcap_hdr = (struct pcap_pkthdr*)PacketBuff; - - if((char*)winpcap_hdr + winpcap_hdr->caplen + sizeof(struct pcap_pkthdr) > - EndOfUserBuff ) - { - // Malformed buffer - return 0; - } - - if (sync) - ts = winpcap_hdr->ts; - - while( true ){ - - if (*p_stop) - return (char*)winpcap_hdr - (char*)PacketBuff; - - if(winpcap_hdr->caplen ==0 || winpcap_hdr->caplen > 65536) - { - // Malformed header - return 0; - } - - // Send the packet - ret = pcap_sendpacket(p, - (unsigned char*)winpcap_hdr + sizeof(struct pcap_pkthdr), - winpcap_hdr->caplen); - - if(ret < 0){ - // Error sending the packet - return (char*)winpcap_hdr - (char*)PacketBuff; - } - - if (p_pkts) (*p_pkts)++; - if (p_bytes) (*p_bytes) += winpcap_hdr->caplen; - - // Step to the next packet in the buffer - //(char*)winpcap_hdr += winpcap_hdr->caplen + sizeof(struct pcap_pkthdr); - winpcap_hdr = (struct pcap_pkthdr*) ((char*)winpcap_hdr + - winpcap_hdr->caplen + sizeof(struct pcap_pkthdr)); - - // Check if the end of the user buffer has been reached - if( (char*)winpcap_hdr >= EndOfUserBuff ) - { - return (char*)winpcap_hdr - (char*)PacketBuff; - } - - if (sync) - { - long usec = (winpcap_hdr->ts.tv_sec-ts.tv_sec)*1000000 + - (winpcap_hdr->ts.tv_usec - ts.tv_usec); - - if (usec) - { - (*pf_usleep)(usec); - ts = winpcap_hdr->ts; - } - } - } -} diff --git a/server/pcapextra.h b/server/pcapextra.h index 5c597df..e0dd23b 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -1,36 +1,12 @@ #ifndef _PCAP_EXTRA_H #define _PCAP_EXTRA_H -#include -#include - -#include "pcap.h" - -struct ost_pcap_send_queue -{ - pcap_send_queue *sendQueue; - //! Used to track num of packets (and their sizes) in the - // send queue. Also used to find out actual num of pkts sent - // in case of partial send in pcap_sendqueue_transmit() - QList sendQueueCumLen; -}; - -// Common for all OS - *nix or Win32 -u_int ost_pcap_sendqueue_list_transmit(pcap_t *p, - QList sendQueueList, int returnToQIdx, int sync, - int *p_stop, quint64* p_pkts, quint64* p_bytes, - void (*pf_usleep)(ulong)); - -u_int ost_pcap_sendqueue_transmit (pcap_t *p, - pcap_send_queue *queue, int sync, - int *p_stop, quint64* p_pkts, quint64* p_bytes, - void (*pf_usleep)(ulong)); +#include +#include #ifndef Q_OS_WIN32 -// Only for non Win32 - -#define PCAP_OPENFLAG_PROMISCUOUS 1 +//#define PCAP_OPENFLAG_PROMISCUOUS 1 struct pcap_send_queue { @@ -39,15 +15,11 @@ struct pcap_send_queue char *buffer; }; -int pcap_setmode(pcap_t *p, int mode); -#define MODE_STAT 1 - pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); void pcap_sendqueue_destroy (pcap_send_queue *queue); int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); - #endif #endif diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..86a5086 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,407 @@ +#include "pcapport.h" + +pcap_if_t *PcapPort::deviceList_ = NULL; + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + delete capturer_; + delete transmitter_; + delete monitorTx_; + delete monitorRx_; +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE]; + + direction_ = direction; + isDirectional_ = true; + stats_ = stats; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + Q_ASSERT(false); + } + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); +} + +void PcapPort::PortMonitor::run() +{ + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + returnToQIdx_ = -1; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + usingInternalHandle_ = false; +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for sendQueueList + while(sendQueueList_.size()) + { + pcap_send_queue *sq = sendQueueList_.takeFirst(); + pcap_sendqueue_destroy(sq); + } +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + pcap_send_queue *sendQ; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = usec; + + if (sendQueueList_.size()) + sendQ = sendQueueList_.last(); + else + sendQ = pcap_sendqueue_alloc(1*1024*1024); + + // Not enough space? Alloc another one! + if ((sendQ->len + length + sizeof(pcap_pkthdr)) > sendQ->maxlen) + { + sendQueueList_.append(sendQ); + + //! \todo (LOW): calculate sendqueue size + sendQ = pcap_sendqueue_alloc(1*1024*1024); + } + + if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) + op = false; + + sendQueueList_.append(sendQ); + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_); + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + + for(i = 0; i < sendQueueList_.size(); i++) + { + int ret; +_restart: + ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); + + if (ret < 0) + return; + + //! \todo (HIGH): Timing between subsequent sendQueues + } + + if (returnToQIdx_ >= 0) + { + i = returnToQIdx_; + + //! \todo (HIGH) 1s fixed; Change this to ipg of last stream + QThread::usleep(1000000); + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, int sync) +{ + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + if (sync) + ts = hdr->ts; + + while (1) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (stop_) + { + stop_ = false; + return -2; + } + + if(pktLen > 0) + pcap_sendpacket(p, pkt, pktLen); + + stats_->txPkts++; + stats_->txBytes += pktLen; + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + // Check if the end of the user buffer has been reached + if((char*) hdr >= end) + return 0; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + if (usec) + { + QThread::usleep(usec); + ts = hdr->ts; + } + } + } +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } + + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); + if (handle_ == NULL) + { + qDebug("Error opening port %s: %s\n", + device_.toAscii().constData(), pcap_geterr(handle_)); + return; + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + stop_ = false; + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; +} + +void PcapPort::PortCapturer::stop() +{ + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..8fee855 --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,121 @@ +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false); + } + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, usec, packet, length); + } + virtual void setPacketListLoopMode(bool loop) { + transmitter_->setPacketListLoopMode(loop); + } + + virtual void startTransmit() { + if (isDirty()) + updatePacketList(); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + protected: + AbstractPort::PortStats *stats_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + void clearPacketList(); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop) { + returnToQIdx_ = loop ? 0 : -1; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + + QList sendQueueList_; + int returnToQIdx_; + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..bd7fd78 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,57 @@ +#include "portmanager.h" + +#include + +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + +#ifdef Q_OS_WIN32 + port = new WinPcapPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + port->init(); + portList_.append(port); + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + } + + pcap_freealldevs(deviceList); + + return; +} + +PortManager::~PortManager() +{ +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} + + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..c71c6a6 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,26 @@ +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..7e04a42 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,144 @@ +#include "winpcapport.h" + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + QThread::msleep(1000); + } +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..100ad2a --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,31 @@ +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ From c7d90ff1ab040cbd6fb4b3e940340ebfb5c228f9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 28 Dec 2009 08:31:28 +0000 Subject: [PATCH 036/294] - All tabs converted to spaces in all files - .vimrc added to reflect settings used in the project --- .vimrc | 5 + client/dumpview.cpp | 530 ++++++------ client/dumpview.h | 54 +- client/hexlineedit.cpp | 18 +- client/hexlineedit.h | 2 +- client/main.cpp | 4 +- client/mainwindow.cpp | 52 +- client/mainwindow.h | 18 +- client/modeltest.cpp | 6 +- client/ostinato.pro | 76 +- client/packetmodel.cpp | 300 +++---- client/packetmodel.h | 42 +- client/port.cpp | 208 ++--- client/port.h | 124 +-- client/portgroup.cpp | 914 ++++++++++----------- client/portgroup.h | 130 +-- client/portgrouplist.cpp | 92 +-- client/portgrouplist.h | 42 +- client/portmodel.cpp | 396 ++++----- client/portmodel.h | 48 +- client/portstatsfilterdialog.cpp | 144 ++-- client/portstatsfilterdialog.h | 28 +- client/portstatsmodel.cpp | 366 ++++----- client/portstatsmodel.h | 142 ++-- client/portstatswindow.cpp | 188 ++--- client/portstatswindow.h | 26 +- client/portswindow.cpp | 544 ++++++------- client/portswindow.h | 52 +- client/stream.cpp | 58 +- client/stream.h | 10 +- client/streamconfigdialog.cpp | 1306 +++++++++++++++--------------- client/streamconfigdialog.h | 126 +-- client/streamlistdelegate.cpp | 230 +++--- client/streamlistdelegate.h | 20 +- client/streammodel.cpp | 350 ++++---- client/streammodel.h | 66 +- common/abstractprotocol.cpp | 588 +++++++------- common/abstractprotocol.h | 150 ++-- common/comboprotocol.h | 298 +++---- common/dot2llc.h | 2 +- common/dot2llc.proto | 4 +- common/dot2snap.h | 2 +- common/dot2snap.proto | 4 +- common/dot3.cpp | 150 ++-- common/dot3.h | 54 +- common/dot3.proto | 4 +- common/eth2.cpp | 158 ++-- common/eth2.h | 54 +- common/eth2.proto | 4 +- common/ip4.cpp | 1124 ++++++++++++------------- common/ip4.h | 118 +-- common/ip4.proto | 64 +- common/llc.cpp | 196 ++--- common/llc.h | 58 +- common/llc.proto | 8 +- common/mac.cpp | 392 ++++----- common/mac.h | 76 +- common/mac.proto | 32 +- common/ostproto.pro | 140 ++-- common/payload.cpp | 268 +++--- common/payload.h | 66 +- common/payload.proto | 22 +- common/protocol.proto | 240 +++--- common/protocollist.cpp | 4 +- common/protocollist.h | 2 +- common/protocollistiterator.cpp | 82 +- common/protocollistiterator.h | 36 +- common/protocolmanager.cpp | 164 ++-- common/protocolmanager.h | 28 +- common/sample.cpp | 634 +++++++-------- common/sample.h | 84 +- common/sample.proto | 14 +- common/snap.cpp | 190 ++--- common/snap.h | 58 +- common/snap.proto | 6 +- common/streambase.cpp | 368 ++++----- common/streambase.h | 138 ++-- common/svlan.cpp | 26 +- common/svlan.h | 18 +- common/svlan.proto | 2 +- common/tcp.cpp | 690 ++++++++-------- common/tcp.h | 92 +-- common/tcp.proto | 24 +- common/udp.cpp | 420 +++++----- common/udp.h | 68 +- common/udp.proto | 14 +- common/userscript.proto | 4 +- common/vlan.cpp | 304 +++---- common/vlan.h | 64 +- common/vlan.proto | 12 +- common/vlanstack.h | 2 +- common/vlanstack.proto | 4 +- rpc/pbhelper.h | 220 ++--- rpc/pbrpcchannel.cpp | 416 +++++----- rpc/pbrpcchannel.h | 100 +-- rpc/pbrpccommon.h | 56 +- rpc/pbrpccontroller.h | 34 +- rpc/rpcserver.cpp | 338 ++++---- rpc/rpcserver.h | 34 +- server/drone.cpp | 70 +- server/drone.h | 12 +- server/drone.pro | 12 +- server/drone_main.cpp | 20 +- server/pcapextra.cpp | 62 +- server/pcapextra.h | 10 +- 105 files changed, 7967 insertions(+), 7962 deletions(-) create mode 100644 .vimrc diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/client/dumpview.cpp b/client/dumpview.cpp index 3393d5b..d846164 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -4,384 +4,384 @@ DumpView::DumpView(QWidget *parent) { - int w, h; + int w, h; - // NOTE: Monospaced fonts only !!!!!!!!!!! - setFont(QFont("Courier")); - w = fontMetrics().width('X'); - h = fontMetrics().height(); + // NOTE: Monospaced fonts only !!!!!!!!!!! + setFont(QFont("Courier")); + w = fontMetrics().width('X'); + h = fontMetrics().height(); - mLineHeight = h; - mCharWidth = w; + mLineHeight = h; + mCharWidth = w; - mSelectedRow = mSelectedCol = -1; + mSelectedRow = mSelectedCol = -1; - // calculate width for offset column and the whitespace that follows it - // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ - 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"); + // calculate width for offset column and the whitespace that follows it + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); } QModelIndex DumpView::indexAt( const QPoint &point ) const { #if 0 - int x = point.x(); - int row, col; + int x = point.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 = point.y()/mLineHeight; + 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 = point.y()/mLineHeight; - if ((col < 16) && (row < ((data.size()+16)/16))) - { - selrow = row; - selcol = col; - } - else - goto _exit; + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = col; + } + else + goto _exit; - // last row check col - if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16))) - goto _exit; + // last row check col + if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16))) + goto _exit; - qDebug("dumpview::selection(%d, %d)", selrow, selcol); + qDebug("dumpview::selection(%d, %d)", selrow, selcol); - offset = selrow * 16 + selcol; + offset = selrow * 16 + selcol; #if 0 - for(int i = 0; i < model()->rowCount(parent); i++) - { - QModelIndex index = model()->index(i, 0, parent); + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); - if (model()->hasChildren(index)) - indexAtOffset(offset, index); // Non Leaf - else - if ( - dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf - // FIXME: Use RawValueRole instead of UserRole - } + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } #endif } _exit: - // Clear existing selection - selrow = -1; + // Clear existing selection + selrow = -1; #endif - return QModelIndex(); + return QModelIndex(); } void DumpView::scrollTo( const QModelIndex &index, ScrollHint hint ) { - // FIXME: implement scrolling + // FIXME: implement scrolling } QRect DumpView::visualRect( const QModelIndex &index ) const { - // FIXME: calculate actual rect - return rect(); + // FIXME: calculate actual rect + return rect(); } //protected: int DumpView::horizontalOffset() const { - return horizontalScrollBar()->value(); + return horizontalScrollBar()->value(); } bool DumpView::isIndexHidden( const QModelIndex &index ) const { - return false; + return false; } QModelIndex DumpView::moveCursor( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ) + Qt::KeyboardModifiers modifiers ) { - // FIXME(MED): need to implement movement using cursor - return currentIndex(); + // FIXME(MED): need to implement movement using cursor + return currentIndex(); } void DumpView::setSelection( const QRect &rect, - QItemSelectionModel::SelectionFlags flags ) + QItemSelectionModel::SelectionFlags flags ) { - // FIXME(HI): calculate indexes using rect - selectionModel()->select(QModelIndex(), flags); + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); } int DumpView::verticalOffset() const { - return verticalScrollBar()->value(); + return verticalScrollBar()->value(); } QRegion DumpView::visualRegionForSelection( const QItemSelection &selection ) const { - // FIXME(HI) - return QRegion(rect()); + // FIXME(HI) + return QRegion(rect()); } //protected slots: void DumpView::dataChanged( const QModelIndex &topLeft, - const QModelIndex &bottomRight ) + const QModelIndex &bottomRight ) { - // FIXME(HI) - update(); + // FIXME(HI) + update(); } void DumpView::selectionChanged( const QItemSelection &selected, - const QItemSelection &deselected ) + const QItemSelection &deselected ) { - // FIXME(HI) - update(); + // FIXME(HI) + update(); } void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, - QModelIndex parent) + QModelIndex parent) { - // FIXME: Use new enum instead of Qt::UserRole - //! \todo (low): generalize this for any model not just our pkt model + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model - Q_ASSERT(!parent.isValid()); + Q_ASSERT(!parent.isValid()); - qDebug("!!!! %d $$$$", dump.size()); + qDebug("!!!! %d $$$$", dump.size()); - for(int i = 0; i < model()->rowCount(parent); i++) - { - QModelIndex index = model()->index(i, 0, parent); + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); - Q_ASSERT(index.isValid()); + Q_ASSERT(index.isValid()); - // Assumption: protocol data is in bytes (not bits) - qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); - dump.append(model()->data(index, Qt::UserRole).toByteArray()); + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); - } + } - if (selectionModel()->selectedIndexes().size()) - { - int j, bits; - QModelIndex index; + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; - Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); - index = selectionModel()->selectedIndexes().at(0); + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); - if (index.parent().isValid()) - { - // Field + if (index.parent().isValid()) + { + // Field - // SelOfs = SUM(protocol sizes before selected field's protocol) + - // SUM(field sizes before selected field) + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) - selOfs = 0; - j = index.parent().row() - 1; - while (j >= 0) - { - selOfs += model()->data(index.parent().sibling(j,0), - Qt::UserRole).toByteArray().size(); - j--; - } + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } - bits = 0; - j = index.row() - 1; - while (j >= 0) - { - bits += model()->data(index.sibling(j,0), Qt::UserRole+1). - toInt(); - j--; - } - selOfs += bits/8; - selSize = model()->data(index, Qt::UserRole).toByteArray().size(); - } - else - { - // Protocol - selOfs = 0; - j = index.row() - 1; - while (j >= 0) - { - selOfs += model()->data(index.sibling(j,0), Qt::UserRole). - toByteArray().size(); - j--; - } - selSize = model()->data(index, Qt::UserRole).toByteArray().size(); - } - } + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } } // TODO(LOW): rewrite this function - it's a mess! void DumpView::paintEvent(QPaintEvent* event) { - QStylePainter painter(viewport()); - QRect offsetRect = mOffsetPaneTopRect; - QRect dumpRect = mDumpPaneTopRect; - QRect asciiRect = mAsciiPaneTopRect; - QPalette pal = palette(); - static QByteArray data; - //QByteArray ba; - int selOfs = -1, selSize; - int curSelOfs, curSelSize; + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; - qDebug("dumpview::paintEvent"); + qDebug("dumpview::paintEvent"); - // FIXME(LOW): unable to set the self widget's font in constructor - painter.setFont(QFont("Courier")); + // 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))); + // set a white background + painter.fillRect(rect(), QBrush(QColor(Qt::white))); - if (model()) - { - data.clear(); - populateDump(data, selOfs, selSize); - } + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } - // 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; + // 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); + //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); + // 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(" "); - } + // 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(" "); + dumpStr.append(QString("%1").arg((uint)c, 2, 16, QChar('0')). + toUpper()).append(" "); - if (isPrintable(c)) - asciiStr.append(QChar(c)); - else - asciiStr.append(QChar('.')); - } + 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 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); + // display ascii + painter.drawItemText(asciiRect, Qt::AlignLeft | Qt::AlignTop, pal, + true, asciiStr, QPalette::WindowText); - // if no selection, skip selection painting - if (selOfs < 0) - goto _next; + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; - // Check overlap between current row and selection - { - QRect r1(i, 0, qMin(16, data.size()-i), 8); - QRect s1(selOfs, 0, selSize, 8); - if (r1.intersects(s1)) - { - QRect t = r1.intersected(s1); + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); - curSelOfs = t.x(); - curSelSize = t.width(); - } - else - curSelSize = 0; + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; - } + } - // overpaint selection on current row (if any) - if (curSelSize > 0) - { - QRect r; - QString selectedAsciiStr, selectedDumpStr; + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; - qDebug("dumpview::paintEvent - Highlighted (%d, %d)", - curSelOfs, curSelSize); + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); - // construct the dumpStr and asciiStr - for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) - { - unsigned char c = data.at(k); + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); - // extra space after 8 bytes - if (((k+8) % 16) == 0) - { - // Avoid adding space at the start for fields starting - // at second column 8 byte boundary - if (k!=curSelOfs) - { - selectedDumpStr.append(" "); - selectedAsciiStr.append(" "); - } - } + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } - selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, - QChar('0')).toUpper()).append(" "); + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); - if (isPrintable(c)) - selectedAsciiStr.append(QChar(c)); - else - selectedAsciiStr.append(QChar('.')); - } + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } - // display dump - r = dumpRect; - if ((curSelOfs - i) < 8) - r.translate(mCharWidth*(curSelOfs-i)*3, 0); - else - r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); - // adjust width taking care of selection stretching between - // the two 8byte columns - if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) - r.setWidth((curSelSize * 3 + 1) * mCharWidth); - else - r.setWidth((curSelSize * 3) * mCharWidth); + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); - painter.fillRect(r, pal.highlight()); - painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, - true, selectedDumpStr, QPalette::HighlightedText); + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); - // display ascii - r = asciiRect; - if ((curSelOfs - i) < 8) - r.translate(mCharWidth*(curSelOfs-i)*1, 0); - else - r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); - // adjust width taking care of selection stretching between - // the two 8byte columns - if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) - r.setWidth((curSelSize * 1 + 1) * mCharWidth); - else - r.setWidth((curSelSize * 1) * mCharWidth); + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); - painter.fillRect(r, pal.highlight()); - painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, - true, selectedAsciiStr, QPalette::HighlightedText); - } + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } -_next: - // move the rects down - offsetRect.translate(0, mLineHeight); - dumpRect.translate(0, mLineHeight); - asciiRect.translate(0, mLineHeight); - } +_next: + // 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 index e362103..db17002 100644 --- a/client/dumpview.h +++ b/client/dumpview.h @@ -3,40 +3,40 @@ class DumpView: public QAbstractItemView { -public: - DumpView(QWidget *parent=0); +public: + DumpView(QWidget *parent=0); - QModelIndex indexAt( const QPoint &point ) const; - void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); - QRect visualRect( const QModelIndex &index ) const; + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; protected: - int horizontalOffset() const; - bool isIndexHidden( const QModelIndex &index ) const; - QModelIndex moveCursor( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ); - void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); - int verticalOffset() const; - QRegion visualRegionForSelection( const QItemSelection &selection ) const; + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; protected slots: - void dataChanged( const QModelIndex &topLeft, - const QModelIndex &bottomRight ); - void selectionChanged( const QItemSelection &selected, - const QItemSelection &deselected ); - void paintEvent(QPaintEvent *event); + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); private: - void populateDump(QByteArray &dump, int &selOfs, int &selSize, - QModelIndex parent = QModelIndex()); - bool inline isPrintable(char c) - {if ((c > 48) && (c < 126)) return true; else return false; } + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } private: - QRect mOffsetPaneTopRect; - QRect mDumpPaneTopRect; - QRect mAsciiPaneTopRect; - int mSelectedRow, mSelectedCol; - int mLineHeight; - int mCharWidth; + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; }; diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index dcb6c20..5f099d0 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -4,9 +4,9 @@ QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); HexLineEdit::HexLineEdit( QWidget * parent) - : QLineEdit(parent) + : QLineEdit(parent) { - //QLineEdit::QLineEdit(parent); + //QLineEdit::QLineEdit(parent); } void HexLineEdit::focusOutEvent( QFocusEvent *e ) @@ -40,15 +40,15 @@ void HexLineEdit::focusOutEvent( QFocusEvent *e ) emit focusOut(); #else #define uintToHexStr(num, bytesize) \ - QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) - bool isOk; - ulong num; + bool isOk; + ulong num; - qDebug("before = %s\n", text().toAscii().data()); - num = text().remove(QChar(' ')).toULong(&isOk, 16); - setText(uintToHexStr(num, 4)); - qDebug("after = %s\n", text().toAscii().data()); + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); #undef uintToHexStr #endif } diff --git a/client/hexlineedit.h b/client/hexlineedit.h index 7c5811e..937d263 100644 --- a/client/hexlineedit.h +++ b/client/hexlineedit.h @@ -8,7 +8,7 @@ class HexLineEdit : public QLineEdit Q_OBJECT public: // Constructors - HexLineEdit ( QWidget * parent); + HexLineEdit ( QWidget * parent); protected: void focusOutEvent( QFocusEvent *e ); diff --git a/client/main.cpp b/client/main.cpp index 2f9b385..9a9a5d8 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -4,8 +4,8 @@ int main(int argc, char* argv[]) { - QApplication app(argc, argv); - MainWindow mainWin; + QApplication app(argc, argv); + MainWindow mainWin; mainWin.show(); return app.exec(); diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 9b2d12a..2aee434 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -12,54 +12,54 @@ #include #include -PortGroupList *pgl; +PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) - : QMainWindow (parent) + : QMainWindow (parent) { - localServer_ = new QProcess(this); - localServer_->start("drone.exe"); + localServer_ = new QProcess(this); + localServer_->start("drone.exe"); - pgl = new PortGroupList; + pgl = new PortGroupList; - portsWindow = new PortsWindow(pgl, this); - statsWindow = new PortStatsWindow(pgl, this); - portsDock = new QDockWidget(tr("Ports"), this); - statsDock = new QDockWidget(tr("Stats"), this); + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports"), this); + statsDock = new QDockWidget(tr("Stats"), this); - setupUi(this); + setupUi(this); - statsDock->setWidget(statsWindow); + statsDock->setWidget(statsWindow); addDockWidget(Qt::BottomDockWidgetArea, statsDock); - portsDock->setWidget(portsWindow); + portsDock->setWidget(portsWindow); addDockWidget(Qt::TopDockWidgetArea, portsDock); - connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); #if 0 - { - DbgThread *dbg = new DbgThread(pgl); - dbg->start(); - } + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } #endif } MainWindow::~MainWindow() { - delete pgl; - localServer_->terminate(); - localServer_->waitForFinished(); - delete localServer_; + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; } void MainWindow::on_actionHelpAbout_triggered() { - QDialog *aboutDialog = new QDialog; + QDialog *aboutDialog = new QDialog; - Ui::About about; - about.setupUi(aboutDialog); + Ui::About about; + about.setupUi(aboutDialog); - aboutDialog->exec(); + aboutDialog->exec(); - delete aboutDialog; + delete aboutDialog; } diff --git a/client/mainwindow.h b/client/mainwindow.h index 5132385..53c8eac 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -12,21 +12,21 @@ class QProcess; class MainWindow : public QMainWindow, private Ui::MainWindow { - Q_OBJECT + Q_OBJECT private: - QProcess *localServer_; - PortsWindow *portsWindow; - PortStatsWindow *statsWindow; - QDockWidget *portsDock; - QDockWidget *statsDock; + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; public: - MainWindow(QWidget *parent = 0); - ~MainWindow(); + MainWindow(QWidget *parent = 0); + ~MainWindow(); public slots: - void on_actionHelpAbout_triggered(); + void on_actionHelpAbout_triggered(); }; #endif diff --git a/client/modeltest.cpp b/client/modeltest.cpp index 58685b0..2598c58 100644 --- a/client/modeltest.cpp +++ b/client/modeltest.cpp @@ -250,9 +250,9 @@ void ModelTest::parent() // that is the first level index. if (model->rowCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); - qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); - qDebug("topIndex I %llx", topIndex.internalId()); - qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); Q_ASSERT(model->parent(childIndex) == topIndex); } diff --git a/client/ostinato.pro b/client/ostinato.pro index ff54e47..b0dd570 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -10,48 +10,48 @@ unix:LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" RESOURCES += ostinato.qrc HEADERS += \ - dumpview.h \ - hexlineedit.h \ - mainwindow.h \ - packetmodel.h \ - port.h \ - portgroup.h \ - portgrouplist.h \ - portmodel.h \ - portstatsmodel.h \ - portstatsfilterdialog.h \ - portstatswindow.h \ - portswindow.h \ - streamconfigdialog.h \ - streamlistdelegate.h \ - streammodel.h + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h FORMS += \ - about.ui \ - mainwindow.ui \ - portstatsfilter.ui \ - portstatswindow.ui \ - portswindow.ui \ - streamconfigdialog.ui + about.ui \ + mainwindow.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + streamconfigdialog.ui SOURCES += \ - dumpview.cpp \ - stream.cpp \ - hexlineedit.cpp \ - main.cpp \ - mainwindow.cpp \ - packetmodel.cpp \ - port.cpp \ - portgroup.cpp \ - portgrouplist.cpp \ - portmodel.cpp \ - portstatsmodel.cpp \ - portstatsfilterdialog.cpp \ - portstatswindow.cpp \ - portswindow.cpp \ - streamconfigdialog.cpp \ - streamlistdelegate.cpp \ - streammodel.cpp + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp # TODO(LOW): Test only include(modeltest.pri) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 6b7b834..a3aee4d 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -10,211 +10,211 @@ PacketModel::PacketModel(QObject *parent) void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) { - QList currentProtocols; + QList currentProtocols; - iter.toFront(); - while (iter.hasNext()) - currentProtocols.append(iter.next()); + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); - if (mSelectedProtocols != currentProtocols) - { - mSelectedProtocols = currentProtocols; - reset(); - } + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } } int PacketModel::rowCount(const QModelIndex &parent) const { - IndexId parentId; + IndexId parentId; - // qDebug("in %s", __FUNCTION__); + // qDebug("in %s", __FUNCTION__); - // Parent == Invalid i.e. Invisible Root. - // ==> Children are Protocol (Top Level) Items - if (!parent.isValid()) - return mSelectedProtocols.size(); + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); - // Parent - Valid Item - parentId.w = parent.internalId(); - switch(parentId.ws.type) - { - case ITYP_PROTOCOL: - return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); - case ITYP_FIELD: - return 0; - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } - Q_ASSERT(1 == 0); // Unreachable code - qWarning("%s: Catch all - need to investigate", __FUNCTION__); - return 0; // catch all + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%s: Catch all - need to investigate", __FUNCTION__); + return 0; // catch all } int PacketModel::columnCount(const QModelIndex &parent) const { - return 1; + return 1; } QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) const { - QModelIndex index; - IndexId id, parentId; + QModelIndex index; + IndexId id, parentId; - if (!hasIndex(row, col, parent)) - goto _exit; + if (!hasIndex(row, col, parent)) + goto _exit; - // Parent is Invisible Root - // Request for a Protocol Item - if (!parent.isValid()) - { - id.w = 0; - id.ws.type = ITYP_PROTOCOL; - id.ws.protocol = row; - index = createIndex(row, col, id.w); - goto _exit; - } + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } - // Parent is a Valid Item - parentId.w = parent.internalId(); - id.w = parentId.w; - switch(parentId.ws.type) - { - case ITYP_PROTOCOL: - id.ws.type = ITYP_FIELD; - index = createIndex(row, col, id.w); - goto _exit; + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; - case ITYP_FIELD: - Q_ASSERT(1 == 0); // Unreachable code - goto _exit; + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } - Q_ASSERT(1 == 0); // Unreachable code + Q_ASSERT(1 == 0); // Unreachable code _exit: - return index; + return index; } QModelIndex PacketModel::parent(const QModelIndex &index) const { - QModelIndex parentIndex; - IndexId id, parentId; + QModelIndex parentIndex; + IndexId id, parentId; - if (!index.isValid()) - return QModelIndex(); + if (!index.isValid()) + return QModelIndex(); - id.w = index.internalId(); - parentId.w = id.w; - switch(id.ws.type) - { - case ITYP_PROTOCOL: - // return invalid index for invisible root - goto _exit; + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; - case ITYP_FIELD: - parentId.ws.type = ITYP_PROTOCOL; - parentIndex = createIndex(id.ws.protocol, 0, parentId.w); - goto _exit; + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } - Q_ASSERT(1 == 1); // Unreachable code + Q_ASSERT(1 == 1); // Unreachable code _exit: - return parentIndex; + return parentIndex; } QVariant PacketModel::data(const QModelIndex &index, int role) const { - IndexId id; - int fieldIdx = 0; + IndexId id; + int fieldIdx = 0; - if (!index.isValid()) - return QVariant(); + if (!index.isValid()) + return QVariant(); - id.w = index.internalId(); + id.w = index.internalId(); - if (id.ws.type == ITYP_FIELD) - { - const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); - int n = index.row() + 1; + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; - while (n) - { - if (!(p->fieldFlags(fieldIdx).testFlag( - AbstractProtocol::FieldIsMeta))) - n--; - fieldIdx++; - } - fieldIdx--; - } + while (n) + { + if (!(p->fieldFlags(fieldIdx).testFlag( + AbstractProtocol::FieldIsMeta))) + n--; + fieldIdx++; + } + fieldIdx--; + } - // FIXME(HI): Relook at this completely - if (role == Qt::UserRole) - { - switch(id.ws.type) - { - case ITYP_PROTOCOL: - qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); - return mSelectedProtocols.at(id.ws.protocol)-> - protocolFrameValue(); + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData( - fieldIdx, AbstractProtocol::FieldFrameValue); + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - return QByteArray(); - } + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } - // FIXME: Use a new enum here instead of UserRole - if (role == (Qt::UserRole+1)) - { - switch(id.ws.type) - { - case ITYP_PROTOCOL: - return mSelectedProtocols.at(id.ws.protocol)-> - protocolFrameValue().size(); + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData( - fieldIdx, AbstractProtocol::FieldBitSize); + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - return QVariant(); - } + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } - if (role != Qt::DisplayRole) - return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); - switch(id.ws.type) - { - case ITYP_PROTOCOL: - return QString("%1 (%2)") - .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) - .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, - AbstractProtocol::FieldName).toString() + QString(" : ") + - mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, - AbstractProtocol::FieldTextValue).toString(); + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } - Q_ASSERT(1 == 1); // Unreachable code + Q_ASSERT(1 == 1); // Unreachable code - return QVariant(); + return QVariant(); } diff --git a/client/packetmodel.h b/client/packetmodel.h index c6b0cfc..2568724 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -10,31 +10,31 @@ class PacketModel: public QAbstractItemModel { public: - PacketModel(QObject *parent = 0); - void setSelectedProtocols(ProtocolListIterator &iter); + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const { return QVariant(); } ; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const { return QVariant(); } ; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; private: - typedef union _IndexId - { - quint32 w; - struct - { - quint16 type; -#define ITYP_PROTOCOL 1 -#define ITYP_FIELD 2 - quint16 protocol; // protocol is valid for both ITYPs - } ws; - } IndexId; + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; - QList mSelectedProtocols; + QList mSelectedProtocols; }; #endif diff --git a/client/port.cpp b/client/port.cpp index 87f0e07..8f708f1 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -9,15 +9,15 @@ uint Port::mAllocStreamId = 0; uint Port::newStreamId() { - return mAllocStreamId++; + return mAllocStreamId++; } Port::Port(quint32 id, quint32 portGroupId) { - mPortId = id; - d.mutable_port_id()->set_id(id); - stats.mutable_port_id()->set_id(id); - mPortGroupId = portGroupId; + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; } Port::~Port() @@ -26,172 +26,172 @@ Port::~Port() void Port::updatePortConfig(OstProto::Port *port) { - d.MergeFrom(*port); + d.MergeFrom(*port); } void Port::updateStreamOrdinalsFromIndex() { - for (int i=0; i < mStreams.size(); i++) - mStreams[i]->setOrdinal(i); + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); } void Port::reorderStreamsByOrdinals() { - qSort(mStreams); + qSort(mStreams); } bool Port::newStreamAt(int index) { - Stream *s = new Stream; + Stream *s = new Stream; - if (index > mStreams.size()) - return false; + if (index > mStreams.size()) + return false; - s->setId(newStreamId()); - mStreams.insert(index, s); - updateStreamOrdinalsFromIndex(); + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); - return true; + return true; } bool Port::deleteStreamAt(int index) { - if (index >= mStreams.size()) - return false; + if (index >= mStreams.size()) + return false; - delete mStreams.takeAt(index); - updateStreamOrdinalsFromIndex(); + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); - return true; + return true; } bool Port::insertStream(uint streamId) { - Stream *s = new Stream; + Stream *s = new Stream; - s->setId(streamId); + s->setId(streamId); - // FIXME(MED): If a stream with id already exists, what do we do? - mStreams.append(s); + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); - // Update mAllocStreamId to take into account the stream id received - // from server - if (mAllocStreamId <= streamId) - mAllocStreamId = streamId + 1; + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; - return true; + return true; } bool Port::updateStream(uint streamId, OstProto::Stream *stream) { - int i, streamIndex; + int i, streamIndex; - for (i = 0; i < mStreams.size(); i++) - { - if (streamId == mStreams[i]->id()) - goto _found; - } + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } - qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); - return false; + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; _found: - streamIndex = i; + streamIndex = i; - mStreams[streamIndex]->protoDataCopyFrom(*stream); - reorderStreamsByOrdinals(); + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); - return true; + return true; } void Port::getDeletedStreamsSinceLastSync( - OstProto::StreamIdList &streamIdList) + OstProto::StreamIdList &streamIdList) { - streamIdList.clear_stream_id(); - for (int i = 0; i < mLastSyncStreamList.size(); i++) - { - int j; + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; - for (j = 0; j < mStreams.size(); j++) - { - if (mLastSyncStreamList[i] == mStreams[j]->id()) - break; - } + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } - if (j < mStreams.size()) - { - // stream still exists! - continue; - } - else - { - // stream has been deleted since last sync - OstProto::StreamId *s; + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; - s = streamIdList.add_stream_id(); - s->set_id(mLastSyncStreamList.at(i)); - } - } + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } } void Port::getNewStreamsSinceLastSync( - OstProto::StreamIdList &streamIdList) + OstProto::StreamIdList &streamIdList) { - streamIdList.clear_stream_id(); - for (int i = 0; i < mStreams.size(); i++) - { - if (mLastSyncStreamList.contains(mStreams[i]->id())) - { - // existing stream! - continue; - } - else - { - // new stream! - OstProto::StreamId *s; + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; - s = streamIdList.add_stream_id(); - s->set_id(mStreams[i]->id()); - } - } + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } } void Port::getModifiedStreamsSinceLastSync( - OstProto::StreamConfigList &streamConfigList) + OstProto::StreamConfigList &streamConfigList) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - //streamConfigList.mutable_port_id()->set_id(mPortId); - for (int i = 0; i < mStreams.size(); i++) - { - OstProto::Stream *s; + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; - s = streamConfigList.add_stream(); - mStreams[i]->protoDataCopyInto(*s); - } - qDebug("Done %s", __FUNCTION__); + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); } void Port::when_syncComplete() { - qSort(mStreams); + qSort(mStreams); - mLastSyncStreamList.clear(); - for (int i=0; iid()); + mLastSyncStreamList.clear(); + for (int i=0; iid()); } void Port::updateStats(OstProto::PortStats *portStats) { - OstProto::PortState oldState; + OstProto::PortState oldState; - oldState = stats.state(); - stats.MergeFrom(*portStats); + oldState = stats.state(); + stats.MergeFrom(*portStats); - if (oldState.link_state() != stats.state().link_state()) - { - qDebug("portstate changed"); - emit portDataChanged(mPortGroupId, mPortId); - } + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } } diff --git a/client/port.h b/client/port.h index 9be12ed..09e025a 100644 --- a/client/port.h +++ b/client/port.h @@ -10,88 +10,88 @@ class Port : public QObject { - Q_OBJECT + Q_OBJECT - static uint mAllocStreamId; - OstProto::Port d; - OstProto::PortStats stats; + static uint mAllocStreamId; + OstProto::Port d; + OstProto::PortStats stats; - // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' - quint32 mPortId; - quint32 mPortGroupId; - QString mUserAlias; // user defined + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined - QList mLastSyncStreamList; - QList mStreams; // sorted by stream's ordinal value + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value - uint newStreamId(); - void updateStreamOrdinalsFromIndex(); - void reorderStreamsByOrdinals(); + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); public: - enum AdminStatus { AdminDisable, AdminEnable }; - enum ControlMode { ControlShared, ControlExclusive }; + enum AdminStatus { AdminDisable, AdminEnable }; + enum ControlMode { ControlShared, ControlExclusive }; - // FIXME(HIGH): default args is a hack for QList operations on Port - Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); - ~Port(); + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); - quint32 portGroupId() const { return mPortGroupId; } - const QString& userAlias() const { return mUserAlias; } + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } - quint32 id() const - { return d.port_id().id(); } - const QString name() const - { return QString().fromStdString(d.name()); } - const QString description() const - { return QString().fromStdString(d.description()); } - AdminStatus adminStatus() - { return (d.is_enabled()?AdminEnable:AdminDisable); } - ControlMode controlMode() - { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + ControlMode controlMode() + { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } - //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } - void setAlias(QString &alias) { mUserAlias = alias; } - //void setExclusive(bool flag); + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); - int numStreams() { return mStreams.size(); } - Stream* streamByIndex(int index) - { - Q_ASSERT(index < mStreams.size()); - return mStreams[index]; - } - OstProto::LinkState linkState() - { return stats.state().link_state(); } + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } - OstProto::PortStats getStats() { return stats; } + OstProto::PortStats getStats() { return stats; } - // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal - void updatePortConfig(OstProto::Port *port); - - //! Used by StreamModel - //@{ - bool newStreamAt(int index); - bool deleteStreamAt(int index); - //@} + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index); + bool deleteStreamAt(int index); + //@} - //! Used by MyService::Stub to update from config received from server - //@{ - bool insertStream(uint streamId); - bool updateStream(uint streamId, OstProto::Stream *stream); - //@} + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} - void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); - void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); - void getModifiedStreamsSinceLastSync( - OstProto::StreamConfigList &streamConfigList); + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); - void when_syncComplete(); + void when_syncComplete(); - void updateStats(OstProto::PortStats *portStats); + void updateStats(OstProto::PortStats *portStats); signals: - void portDataChanged(int portGroupId, int portId); + void portDataChanged(int portGroupId, int portId); }; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 4a2843c..e26d2a1 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -4,45 +4,45 @@ #include "portgroup.h" -quint32 PortGroup::mPortGroupAllocId = 0; +quint32 PortGroup::mPortGroupAllocId = 0; PortGroup::PortGroup(QHostAddress ip, quint16 port) { - // Allocate an id for self - mPortGroupId = PortGroup::mPortGroupAllocId++; + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; - rpcChannel = new PbRpcChannel(ip, port); + rpcChannel = new PbRpcChannel(ip, port); - /*! - \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation - as implemented currently, if a RPC is invoked before the previous completes, - rpc controller is overwritten due to the Reset() call - maybe we need to pass the - pointer to the controller to the callback function also? - */ - rpcController = new PbRpcController; - rpcControllerStats = new PbRpcController; - isGetStatsPending_ = false; - serviceStub = new OstProto::OstService::Stub(rpcChannel, - OstProto::OstService::STUB_OWNS_CHANNEL); + /*! + \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation + as implemented currently, if a RPC is invoked before the previous completes, + rpc controller is overwritten due to the Reset() call - maybe we need to pass the + pointer to the controller to the callback function also? + */ + rpcController = new PbRpcController; + rpcControllerStats = new PbRpcController; + isGetStatsPending_ = false; + serviceStub = new OstProto::OstService::Stub(rpcChannel, + OstProto::OstService::STUB_OWNS_CHANNEL); - // FIXME(LOW):Can't for my life figure out why this ain't working! - //QMetaObject::connectSlotsByName(this); - connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(on_rpcChannel_stateChanged())); - connect(rpcChannel, SIGNAL(connected()), - this, SLOT(on_rpcChannel_connected())); - connect(rpcChannel, SIGNAL(disconnected()), - this, SLOT(on_rpcChannel_disconnected())); - connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged())); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); } PortGroup::~PortGroup() { - qDebug("PortGroup Destructor"); - // Disconnect and free rpc channel etc. - PortGroup::disconnectFromHost(); - delete serviceStub; + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; } @@ -51,668 +51,668 @@ PortGroup::~PortGroup() // ------------------------------------------------ void PortGroup::on_rpcChannel_stateChanged() { - qDebug("state changed"); - emit portGroupDataChanged(mPortGroupId); + qDebug("state changed"); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::on_rpcChannel_connected() { - OstProto::Void void_; - OstProto::PortIdList *portIdList; - - qDebug("connected\n"); - emit portGroupDataChanged(mPortGroupId); + OstProto::Void void_; + OstProto::PortIdList *portIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); - qDebug("requesting portlist ..."); - portIdList = new OstProto::PortIdList(); - rpcController->Reset(); - serviceStub->getPortIdList(rpcController, &void_, portIdList, - NewCallback(this, &PortGroup::processPortIdList, portIdList)); + qDebug("requesting portlist ..."); + portIdList = new OstProto::PortIdList(); + rpcController->Reset(); + serviceStub->getPortIdList(rpcController, &void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, portIdList)); } void PortGroup::on_rpcChannel_disconnected() { - qDebug("disconnected\n"); - emit portListAboutToBeChanged(mPortGroupId); + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); - while (!mPorts.isEmpty()) - delete mPorts.takeFirst(); + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); - emit portListChanged(mPortGroupId); - emit portGroupDataChanged(mPortGroupId); + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { - qDebug("error\n"); - emit portGroupDataChanged(mPortGroupId); + qDebug("error\n"); + emit portGroupDataChanged(mPortGroupId); } void PortGroup::when_configApply(int portIndex, uint *cookie) { - uint *op; - OstProto::Ack *ack; + uint *op; + OstProto::Ack *ack; - Q_ASSERT(portIndex < mPorts.size()); + Q_ASSERT(portIndex < mPorts.size()); - if (state() != QAbstractSocket::ConnectedState) - { - if (cookie != NULL) - delete cookie; - return; - } + if (state() != QAbstractSocket::ConnectedState) + { + if (cookie != NULL) + delete cookie; + return; + } - if (cookie == NULL) - { - // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] - // cookie[1]: *ack - cookie = new uint[2]; - ack = new OstProto::Ack; + if (cookie == NULL) + { + // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] + // cookie[1]: *ack + cookie = new uint[2]; + ack = new OstProto::Ack; - cookie[0] = (uint) 0; - cookie[1] = (uint) ack; - } - else - { - ack = (OstProto::Ack*) cookie[1]; - } + cookie[0] = (uint) 0; + cookie[1] = (uint) ack; + } + else + { + ack = (OstProto::Ack*) cookie[1]; + } - Q_ASSERT(cookie != NULL); - op = &cookie[0]; + Q_ASSERT(cookie != NULL); + op = &cookie[0]; - switch (*op) - { - case 0: - { - OstProto::StreamIdList streamIdList; + switch (*op) + { + case 0: + { + OstProto::StreamIdList streamIdList; - qDebug("applying 'deleted streams' ..."); + qDebug("applying 'deleted streams' ..."); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); - (*op)++; - rpcController->Reset(); - serviceStub->deleteStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } + (*op)++; + rpcController->Reset(); + serviceStub->deleteStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } - case 1: - { - OstProto::StreamIdList streamIdList; + case 1: + { + OstProto::StreamIdList streamIdList; - qDebug("applying 'new streams' ..."); + qDebug("applying 'new streams' ..."); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); - (*op)++; - rpcController->Reset(); - serviceStub->addStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } + (*op)++; + rpcController->Reset(); + serviceStub->addStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } - case 2: - { - OstProto::StreamConfigList streamConfigList; + case 2: + { + OstProto::StreamConfigList streamConfigList; - qDebug("applying 'modified streams' ..."); + qDebug("applying 'modified streams' ..."); - streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); + streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); - (*op)++; - rpcController->Reset(); - serviceStub->modifyStream(rpcController, &streamConfigList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } + (*op)++; + rpcController->Reset(); + serviceStub->modifyStream(rpcController, &streamConfigList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } - case 3: - qDebug("apply completed"); - mPorts[portIndex]->when_syncComplete(); - delete cookie; - break; + case 3: + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + delete cookie; + break; - default: - qDebug("%s: Unknown Op!!!", __FUNCTION__); - break; - } + default: + qDebug("%s: Unknown Op!!!", __FUNCTION__); + break; + } } void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) { - qDebug("got a portlist ..."); + qDebug("got a portlist ..."); - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } - emit portListAboutToBeChanged(mPortGroupId); + emit portListAboutToBeChanged(mPortGroupId); - for(int i = 0; i < portIdList->port_id_size(); i++) - { - Port *p; - - p = new Port(portIdList->port_id(i).id(), mPortGroupId); - connect(p, SIGNAL(portDataChanged(int, int)), - this, SIGNAL(portGroupDataChanged(int, int))); - qDebug("before port append\n"); - mPorts.append(p); - } + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } - emit portListChanged(mPortGroupId); + emit portListChanged(mPortGroupId); - this->portIdList.CopyFrom(*portIdList); + this->portIdList.CopyFrom(*portIdList); - // Request PortConfigList - { - OstProto::PortConfigList *portConfigList; - - qDebug("requesting port config list ..."); - portConfigList = new OstProto::PortConfigList(); - rpcController->Reset(); - serviceStub->getPortConfig(rpcController, - portIdList, portConfigList, NewCallback(this, - &PortGroup::processPortConfigList, portConfigList)); - } + // Request PortConfigList + { + OstProto::PortConfigList *portConfigList; + + qDebug("requesting port config list ..."); + portConfigList = new OstProto::PortConfigList(); + rpcController->Reset(); + serviceStub->getPortConfig(rpcController, + portIdList, portConfigList, NewCallback(this, + &PortGroup::processPortConfigList, portConfigList)); + } - goto _exit; + goto _exit; _error_exit: _exit: - delete portIdList; + delete portIdList; } void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } - emit portListAboutToBeChanged(mPortGroupId); + emit portListAboutToBeChanged(mPortGroupId); - for(int i = 0; i < portConfigList->port_size(); i++) - { - uint id; + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; - id = portConfigList->port(i).port_id().id(); - // FIXME: don't mix port id & index into mPorts[] - mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); - } + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } - emit portListChanged(mPortGroupId); + emit portListChanged(mPortGroupId); - // FIXME: check if we need new signals since we are not changing the - // number of ports, just the port data + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data - if (numPorts() > 0) - getStreamIdList(); + if (numPorts() > 0) + getStreamIdList(); _error_exit: - delete portConfigList; + delete portConfigList; } void PortGroup::getStreamIdList(int portIndex, - OstProto::StreamIdList *streamIdList) + OstProto::StreamIdList *streamIdList) { - ::OstProto::PortId portId; + ::OstProto::PortId portId; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (streamIdList == NULL) - { - // First invocation (uses default params) - - // request StreamIdList for first port + if (streamIdList == NULL) + { + // First invocation (uses default params) - + // request StreamIdList for first port - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); - streamIdList = new ::OstProto::StreamIdList(); + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); + streamIdList = new ::OstProto::StreamIdList(); - goto _request; - } + goto _request; + } - qDebug("got a streamIdlist ..."); + qDebug("got a streamIdlist ..."); - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; // FIXME(MED): Partial RPC - } + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; // FIXME(MED): Partial RPC + } - Q_ASSERT(portIndex < numPorts()); + Q_ASSERT(portIndex < numPorts()); - if (streamIdList->port_id().id() != mPorts[portIndex]->id()) - { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), - portIndex); - goto _next_port; // FIXME(MED): Partial RPC - } + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), + portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } - // FIXME(MED): need to mPorts.clear()??? - for(int i = 0; i < streamIdList->stream_id_size(); i++) - { - uint streamId; + // FIXME(MED): need to mPorts.clear()??? + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; - streamId = streamIdList->stream_id(i).id(); - mPorts[portIndex]->insertStream(streamId); - } + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } _next_port: - // FIXME(HI): ideally we shd use signals/slots but this means - // we will have to use Port* instead of Port with QList<> - - // need to find a way for this - mPorts[portIndex]->when_syncComplete(); - portIndex++; - if (portIndex >= numPorts()) - { - // We're done for all ports !!! + // FIXME(HI): ideally we shd use signals/slots but this means + // we will have to use Port* instead of Port with QList<> - + // need to find a way for this + mPorts[portIndex]->when_syncComplete(); + portIndex++; + if (portIndex >= numPorts()) + { + // We're done for all ports !!! - // FIXME(HI): some way to reset streammodel + // FIXME(HI): some way to reset streammodel - delete streamIdList; + delete streamIdList; - if (numPorts() > 0) - getStreamConfigList(); + if (numPorts() > 0) + getStreamConfigList(); - goto _exit; - } + goto _exit; + } _request: - portId.set_id(mPorts[portIndex]->id()); - streamIdList->Clear(); + portId.set_id(mPorts[portIndex]->id()); + streamIdList->Clear(); - rpcController->Reset(); - serviceStub->getStreamIdList(rpcController, &portId, streamIdList, - NewCallback(this, &PortGroup::getStreamIdList, - portIndex, streamIdList)); + rpcController->Reset(); + serviceStub->getStreamIdList(rpcController, &portId, streamIdList, + NewCallback(this, &PortGroup::getStreamIdList, + portIndex, streamIdList)); - goto _exit; + goto _exit; _exit: - return; + return; } void PortGroup::getStreamConfigList(int portIndex, - OstProto::StreamConfigList *streamConfigList) + OstProto::StreamConfigList *streamConfigList) { - OstProto::StreamIdList streamIdList; + OstProto::StreamIdList streamIdList; - qDebug("In %s", __PRETTY_FUNCTION__); + qDebug("In %s", __PRETTY_FUNCTION__); - if (streamConfigList == NULL) - { - // First invocation using default params - // - request for first port + if (streamConfigList == NULL) + { + // First invocation using default params + // - request for first port - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); - streamConfigList = new OstProto::StreamConfigList; + streamConfigList = new OstProto::StreamConfigList; - goto _request; - } + goto _request; + } - qDebug("got a streamconfiglist"); + qDebug("got a streamconfiglist"); - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; - } + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; + } - Q_ASSERT(portIndex < numPorts()); + Q_ASSERT(portIndex < numPorts()); - if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) - { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamConfigList->port_id().id(), - mPorts[portIndex]->id(), portIndex); - goto _next_port; // FIXME(MED): Partial RPC - } + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamConfigList->port_id().id(), + mPorts[portIndex]->id(), portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } - // FIXME(MED): need to mStreams.clear()??? - for(int i = 0; i < streamConfigList->stream_size(); i++) - { - uint streamId; + // FIXME(MED): need to mStreams.clear()??? + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; - streamId = streamConfigList->stream(i).stream_id().id(); - mPorts[portIndex]->updateStream(streamId, - streamConfigList->mutable_stream(i)); - } + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } _next_port: - portIndex++; + portIndex++; - if (portIndex >= numPorts()) - { - // We're done for all ports !!! + if (portIndex >= numPorts()) + { + // We're done for all ports !!! - // FIXME(HI): some way to reset streammodel + // FIXME(HI): some way to reset streammodel - delete streamConfigList; - goto _exit; - } + delete streamConfigList; + goto _exit; + } _request: - qDebug("requesting stream config list ..."); + qDebug("requesting stream config list ..."); - streamIdList.Clear(); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) - { - OstProto::StreamId *s; + streamIdList.Clear(); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s; - s = streamIdList.add_stream_id(); - s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); - } - streamConfigList->Clear(); + s = streamIdList.add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + streamConfigList->Clear(); - rpcController->Reset(); - serviceStub->getStreamConfig(rpcController, - &streamIdList, streamConfigList, NewCallback(this, - &PortGroup::getStreamConfigList, portIndex, streamConfigList)); + rpcController->Reset(); + serviceStub->getStreamConfig(rpcController, + &streamIdList, streamConfigList, NewCallback(this, + &PortGroup::getStreamConfigList, portIndex, streamConfigList)); _exit: - return; + return; } void PortGroup::processModifyStreamAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - qDebug("Modify Successful!!"); + qDebug("Modify Successful!!"); - // TODO(HI): Apply Button should now be disabled???!!!!??? + // TODO(HI): Apply Button should now be disabled???!!!!??? } void PortGroup::startTx(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - return; + if (state() != QAbstractSocket::ConnectedState) + return; - ack = new OstProto::Ack; - if (portList == NULL) - goto _exit; - else - { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - } + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } - serviceStub->startTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartTxAck, ack)); + serviceStub->startTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, ack)); _exit: - return; + return; } void PortGroup::stopTx(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - goto _exit; + if (state() != QAbstractSocket::ConnectedState) + goto _exit; - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; - ack = new OstProto::Ack; + ack = new OstProto::Ack; - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } - rpcController->Reset(); - serviceStub->stopTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopTxAck, ack)); + rpcController->Reset(); + serviceStub->stopTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, ack)); _exit: - return; + return; } void PortGroup::startCapture(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - return; + if (state() != QAbstractSocket::ConnectedState) + return; - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; - ack = new OstProto::Ack; + ack = new OstProto::Ack; - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } - rpcController->Reset(); - serviceStub->startCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartCaptureAck, ack)); + rpcController->Reset(); + serviceStub->startCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, ack)); _exit: - return; + return; } void PortGroup::stopCapture(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - return; + if (state() != QAbstractSocket::ConnectedState) + return; - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; - ack = new OstProto::Ack; - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + ack = new OstProto::Ack; + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } - rpcController->Reset(); - serviceStub->stopCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopCaptureAck, ack)); + rpcController->Reset(); + serviceStub->stopCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, ack)); _exit: - return; + return; } void PortGroup::viewCapture(QList *portList) { - static QTemporaryFile *capFile = NULL; + static QTemporaryFile *capFile = NULL; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - goto _exit; + if (state() != QAbstractSocket::ConnectedState) + goto _exit; - if ((portList == NULL) || (portList->size() != 1)) - goto _exit; + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; - if (capFile) - delete capFile; + if (capFile) + delete capFile; - /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is - not working - it fails everytime */ - capFile = new QTemporaryFile(); - capFile->open(); - qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is + not working - it fails everytime */ + capFile = new QTemporaryFile(); + capFile->open(); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId portId; - OstProto::CaptureBuffer *buf; + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId portId; + OstProto::CaptureBuffer *buf; - portId.set_id(portList->at(i)); + portId.set_id(portList->at(i)); - buf = new OstProto::CaptureBuffer; - rpcController->Reset(); - rpcController->setBinaryBlob(capFile); - serviceStub->getCaptureBuffer(rpcController, &portId, buf, - NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); - } + buf = new OstProto::CaptureBuffer; + rpcController->Reset(); + rpcController->setBinaryBlob(capFile); + serviceStub->getCaptureBuffer(rpcController, &portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); + } _exit: - return; + return; } -void PortGroup::processStartTxAck(OstProto::Ack *ack) +void PortGroup::processStartTxAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - delete ack; + delete ack; } -void PortGroup::processStopTxAck(OstProto::Ack *ack) +void PortGroup::processStopTxAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - delete ack; + delete ack; } -void PortGroup::processStartCaptureAck(OstProto::Ack *ack) +void PortGroup::processStartCaptureAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - delete ack; + delete ack; } -void PortGroup::processStopCaptureAck(OstProto::Ack *ack) +void PortGroup::processStopCaptureAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - delete ack; + delete ack; } void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - capFile->flush(); - capFile->close(); + capFile->flush(); + capFile->close(); - if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", - QStringList() << capFile->fileName())) - qDebug("Failed starting Wireshark"); + if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", + QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); - delete buf; + delete buf; } void PortGroup::getPortStats() { - OstProto::PortStatsList *portStatsList; + OstProto::PortStatsList *portStatsList; - //qDebug("In %s", __FUNCTION__); + //qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - return; + if (state() != QAbstractSocket::ConnectedState) + return; - if (isGetStatsPending_) - return; + if (isGetStatsPending_) + return; - portStatsList = new OstProto::PortStatsList; - rpcControllerStats->Reset(); - isGetStatsPending_ = true; - serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, - NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); + portStatsList = new OstProto::PortStatsList; + rpcControllerStats->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, + NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); } void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) { - //qDebug("In %s", __FUNCTION__); + //qDebug("In %s", __FUNCTION__); - if (rpcControllerStats->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } + if (rpcControllerStats->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } - for(int i = 0; i < portStatsList->port_stats_size(); i++) - { - uint id; + for(int i = 0; i < portStatsList->port_stats_size(); i++) + { + uint id; - id = portStatsList->port_stats(i).port_id().id(); - // FIXME: don't mix port id & index into mPorts[] - mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); - } + id = portStatsList->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); + } - emit statsChanged(mPortGroupId); + emit statsChanged(mPortGroupId); _error_exit: - delete portStatsList; - isGetStatsPending_ = false; + delete portStatsList; + isGetStatsPending_ = false; } void PortGroup::clearPortStats(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; + OstProto::PortIdList portIdList; + OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - if (state() != QAbstractSocket::ConnectedState) - return; + if (state() != QAbstractSocket::ConnectedState) + return; - ack = new OstProto::Ack; - if (portList == NULL) - portIdList.CopyFrom(this->portIdList); - else - { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; + ack = new OstProto::Ack; + if (portList == NULL) + portIdList.CopyFrom(this->portIdList); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - } + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } - rpcController->Reset(); - serviceStub->clearStats(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processClearStatsAck, ack)); + rpcController->Reset(); + serviceStub->clearStats(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, ack)); } -void PortGroup::processClearStatsAck(OstProto::Ack *ack) +void PortGroup::processClearStatsAck(OstProto::Ack *ack) { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - // Refresh stats immediately after a stats clear/reset - getPortStats(); + // Refresh stats immediately after a stats clear/reset + getPortStats(); - delete ack; + delete ack; } diff --git a/client/portgroup.h b/client/portgroup.h index f4d8e99..a503910 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -15,104 +15,104 @@ LOW - Allow hostnames in addition to IP Address as "server address" */ -#define DEFAULT_SERVER_PORT 7878 +#define DEFAULT_SERVER_PORT 7878 class QFile; class PortGroup : public QObject { - Q_OBJECT + Q_OBJECT private: - quint32 mPortGroupId; - static quint32 mPortGroupAllocId; - QString mUserAlias; // user defined + quint32 mPortGroupId; + static quint32 mPortGroupAllocId; + QString mUserAlias; // user defined #if 0 // PB - QTcpSocket *mpSocket; - QHostAddress mServerAddress; - quint16 mServerPort; + QTcpSocket *mpSocket; + QHostAddress mServerAddress; + quint16 mServerPort; #endif - PbRpcChannel *rpcChannel; - PbRpcController *rpcController; - PbRpcController *rpcControllerStats; - bool isGetStatsPending_; - ::OstProto::OstService::Stub *serviceStub; + PbRpcChannel *rpcChannel; + PbRpcController *rpcController; + PbRpcController *rpcControllerStats; + bool isGetStatsPending_; + ::OstProto::OstService::Stub *serviceStub; - ::OstProto::PortIdList portIdList; + ::OstProto::PortIdList portIdList; public: // FIXME(HIGH): member access - QList mPorts; + QList mPorts; public: - PortGroup(QHostAddress ip = QHostAddress::LocalHost, - quint16 port = DEFAULT_SERVER_PORT); - ~PortGroup(); + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); - void connectToHost() { rpcChannel->establish(); } - void connectToHost(QHostAddress ip, quint16 port) - { rpcChannel->establish(ip, port); } - void disconnectFromHost() { rpcChannel->tearDown(); } + void connectToHost() { rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { rpcChannel->establish(ip, port); } + void disconnectFromHost() { rpcChannel->tearDown(); } - int numPorts() const { return mPorts.size(); } - quint32 id() const { return mPortGroupId; } + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } - const QString& userAlias() const { return mUserAlias; } - void setUserAlias(QString alias) { mUserAlias = alias; }; + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; - const QHostAddress& serverAddress() const - { return rpcChannel->serverAddress(); } - quint16 serverPort() const - { return rpcChannel->serverPort(); } - QAbstractSocket::SocketState state() const - { return rpcChannel->state(); } + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } - void processPortIdList(OstProto::PortIdList *portIdList); - void processPortConfigList(OstProto::PortConfigList *portConfigList); + void processPortIdList(OstProto::PortIdList *portIdList); + void processPortConfigList(OstProto::PortConfigList *portConfigList); - void getStreamIdList(int portIndex = 0, - OstProto::StreamIdList *streamIdList = NULL); - void getStreamConfigList(int portIndex = 0, - OstProto::StreamConfigList *streamConfigList = NULL); + void getStreamIdList(int portIndex = 0, + OstProto::StreamIdList *streamIdList = NULL); + void getStreamConfigList(int portIndex = 0, + OstProto::StreamConfigList *streamConfigList = NULL); - void processModifyStreamAck(OstProto::Ack *ack); + void processModifyStreamAck(OstProto::Ack *ack); - void startTx(QList *portList = NULL); - void processStartTxAck(OstProto::Ack *ack); - void stopTx(QList *portList = NULL); - void processStopTxAck(OstProto::Ack *ack); + void startTx(QList *portList = NULL); + void processStartTxAck(OstProto::Ack *ack); + void stopTx(QList *portList = NULL); + void processStopTxAck(OstProto::Ack *ack); - void startCapture(QList *portList = NULL); - void processStartCaptureAck(OstProto::Ack *ack); - void stopCapture(QList *portList = NULL); - void processStopCaptureAck(OstProto::Ack *ack); - void viewCapture(QList *portList = NULL); - void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); + void startCapture(QList *portList = NULL); + void processStartCaptureAck(OstProto::Ack *ack); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(OstProto::Ack *ack); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); - void getPortStats(); - void processPortStatsList(OstProto::PortStatsList *portStatsList); - void clearPortStats(QList *portList = NULL); - void processClearStatsAck(OstProto::Ack *ack); + void getPortStats(); + void processPortStatsList(OstProto::PortStatsList *portStatsList); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(OstProto::Ack *ack); signals: - void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); - void portListAboutToBeChanged(quint32 portGroupId); - void portListChanged(quint32 portGroupId); - void statsChanged(quint32 portGroupId); + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); private slots: - void on_rpcChannel_stateChanged(); - void on_rpcChannel_connected(); - void on_rpcChannel_disconnected(); - void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + void on_rpcChannel_stateChanged(); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); public slots: - void when_configApply(int portIndex, uint *cookie = NULL); + void when_configApply(int portIndex, uint *cookie = NULL); #if 0 // PB - void on_rpcChannel_when_dataAvail(); + void on_rpcChannel_when_dataAvail(); #endif private: #if 0 // PB - void ProcessCapabilityInfo(const char *msg, qint32 size); - void ProcessMsg(const char *msg, quint32 size); + void ProcessCapabilityInfo(const char *msg, qint32 size); + void ProcessMsg(const char *msg, quint32 size); #endif }; diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index e5aa69e..3e67d01 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -4,85 +4,85 @@ #include PortGroupList::PortGroupList() - : mPortGroupListModel(this), - mStreamListModel(this), - mPortStatsModel(this, this) + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) { - PortGroup *pg; + PortGroup *pg; - // TODO(LOW): Remove - new ModelTest(getStreamModel()); - new ModelTest(getPortModel()); - new ModelTest(getPortStatsModel()); - - // Add the "Local" Port Group - pg = new PortGroup; - addPortGroup(*pg); + // TODO(LOW): Remove + new ModelTest(getStreamModel()); + new ModelTest(getPortModel()); + new ModelTest(getPortStatsModel()); + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); } bool PortGroupList::isPortGroup(const QModelIndex& index) { - return mPortGroupListModel.isPortGroup(index); + return mPortGroupListModel.isPortGroup(index); } bool PortGroupList::isPort(const QModelIndex& index) { - return mPortGroupListModel.isPort(index); + return mPortGroupListModel.isPort(index); } PortGroup& PortGroupList::portGroup(const QModelIndex& index) { - Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); - return *(mPortGroups[index.row()]); + return *(mPortGroups[index.row()]); } Port& PortGroupList::port(const QModelIndex& index) { - Q_ASSERT(mPortGroupListModel.isPort(index)); - return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); } void PortGroupList::addPortGroup(PortGroup &portGroup) { - mPortGroupListModel.portGroupAboutToBeAppended(); + mPortGroupListModel.portGroupAboutToBeAppended(); - connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), - &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); #if 0 - connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), - &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortGroupListModel, SLOT(triggerLayoutChanged())); + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); #endif - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortGroupListModel, SLOT(when_portListChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortStatsModel, SLOT(when_portListChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); - connect(&portGroup, SIGNAL(statsChanged(quint32)), - &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); - mPortGroups.append(&portGroup); - portGroup.connectToHost(); + mPortGroups.append(&portGroup); + portGroup.connectToHost(); - mPortGroupListModel.portGroupAppended(); + mPortGroupListModel.portGroupAppended(); - mPortStatsModel.when_portListChanged(); + mPortStatsModel.when_portListChanged(); } void PortGroupList::removePortGroup(PortGroup &portGroup) { - mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); - PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); - qDebug("after takeAt()"); - mPortGroupListModel.portGroupRemoved(); + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); - delete pg; + delete pg; - mPortStatsModel.when_portListChanged(); + mPortStatsModel.when_portListChanged(); } //.................... @@ -90,9 +90,9 @@ void PortGroupList::removePortGroup(PortGroup &portGroup) //.................... int PortGroupList::indexOfPortGroup(quint32 portGroupId) { - for (int i = 0; i < mPortGroups.size(); i++) { - if (mPortGroups.value(i)->id() == portGroupId) - return i; - } - return -1; + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; } diff --git a/client/portgrouplist.h b/client/portgrouplist.h index 89256eb..e5e398b 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -13,39 +13,39 @@ class StreamModel; class PortGroupList : public QObject { - Q_OBJECT + Q_OBJECT - friend class PortModel; - friend class StreamModel; - friend class PortStatsModel; + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; - QList mPortGroups; - PortModel mPortGroupListModel; - StreamModel mStreamListModel; - PortStatsModel mPortStatsModel; + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; // Methods public: - PortGroupList(); + PortGroupList(); - PortModel* getPortModel() { return &mPortGroupListModel; } - PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } - StreamModel* getStreamModel() { return &mStreamListModel; } + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } - bool isPortGroup(const QModelIndex& index); - bool isPort(const QModelIndex& index); - PortGroup& portGroup(const QModelIndex& index); - Port& port(const QModelIndex& index); + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); - int numPortGroups() { return mPortGroups.size(); } - PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } - void addPortGroup(PortGroup &portGroup); - void removePortGroup(PortGroup &portGroup); + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); private: - int indexOfPortGroup(quint32 portGroupId); + int indexOfPortGroup(quint32 portGroupId); }; diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 480b642..ff5c482 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -3,260 +3,260 @@ #include #if 0 -#define DBG0(x) qDebug(x) -#define DBG1(x, p1) qDebug(x, (p1)) +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) #else -#define DBG0(x) {} -#define DBG1(x, p1) {} +#define DBG0(x) {} +#define DBG1(x, p1) {} #endif PortModel::PortModel(PortGroupList *p, QObject *parent) - : QAbstractItemModel(parent) + : QAbstractItemModel(parent) { - pgl = p; + pgl = p; } int PortModel::rowCount(const QModelIndex &parent) const { - // qDebug("RowCount Enter\n"); - if (!parent.isValid()) - { - // Top Level Item - //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); - return pgl->mPortGroups.size(); - } - // qDebug("RowCount non top %d, %d, %llx\n", - // parent.row(), parent.column(), parent.internalId()); + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); - quint16 pg = (parent.internalId() >> 16) & 0xFFFF; - quint16 p = parent.internalId() & 0xFFFF; - if (p == 0xFFFF) - { + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { #if 0 // wrong code? - int count = 0; - foreach(PortGroup *pg, pgl->mPortGroups) - { - count += pg->numPorts(); - } - //qDebug("RowCount (Mid) Exit: %d\n", count); - return count; + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; #endif - if (parent.column() == 0) - return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); - else - return 0; - } - else - { - // Leaf Item - return 0; - } + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } } int PortModel::columnCount(const QModelIndex &parent ) const { - return 1; // FIXME: hardcoding + return 1; // FIXME: hardcoding } Qt::ItemFlags PortModel::flags(const QModelIndex &index) const { - return QAbstractItemModel::flags(index); // FIXME: no need for this func + return QAbstractItemModel::flags(index); // FIXME: no need for this func } QVariant PortModel::data(const QModelIndex &index, int role) const { - DBG0("Enter PortModel data\n"); + DBG0("Enter PortModel data\n"); - // Check for a valid index - if (!index.isValid()) - return QVariant(); + // Check for a valid index + if (!index.isValid()) + return QVariant(); - DBG1("PortModel::data(index).row = %d", index.row()); - DBG1("PortModel::data(index).column = %0d", index.column()); - DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); - QModelIndex parent = index.parent(); + QModelIndex parent = index.parent(); - if (!parent.isValid()) - { - // Top Level Item - PortGroup - if ((role == Qt::DisplayRole)) - { - DBG0("Exit PortModel data 1\n"); - return QString("Port Group %1: %2 [%3:%4] (%5)"). - arg(pgl->mPortGroups.at(index.row())->id()). - arg(pgl->mPortGroups.at(index.row())->userAlias()). - arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). - arg(pgl->mPortGroups.at(index.row())->serverPort()). - arg(pgl->mPortGroups.value(index.row())->numPorts()); - } - else if ((role == Qt::DecorationRole)) - { - DBG0("Exit PortModel data 2\n"); - switch(pgl->mPortGroups.at(index.row())->state()) - { - case QAbstractSocket::UnconnectedState: - return QIcon(":/icons/bullet_red.png"); + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); - case QAbstractSocket::HostLookupState: - return QIcon(":/icons/bullet_yellow.png"); + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); - case QAbstractSocket::ConnectingState: - case QAbstractSocket::ClosingState: - return QIcon(":/icons/bullet_orange.png"); + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); - case QAbstractSocket::ConnectedState: - return QIcon(":/icons/bullet_green.png"); + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); - case QAbstractSocket::BoundState: - case QAbstractSocket::ListeningState: - default: - return QIcon(":/icons/bullet_error.png"); - } - } - else - { - DBG0("Exit PortModel data 3\n"); - return QVariant(); - } - } - else - { - // Non Top Level - Port - if ((role == Qt::DisplayRole)) - { - DBG0("Exit PortModel data 4\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 4\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); - return QString("Port %1: %2 [%3] (%4)"). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->id()). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->name()). - arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->description()); - } - else if ((role == Qt::DecorationRole)) - { - DBG0("Exit PortModel data 5\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); - switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) - { - case OstProto::LinkStateUnknown: - return QIcon(":/icons/bullet_white.png"); - case OstProto::LinkStateDown: - return QIcon(":/icons/bullet_red.png"); - case OstProto::LinkStateUp: - return QIcon(":/icons/bullet_green.png"); - default: - qFatal("unexpected/unimplemented port oper state"); - } - } - else - { - DBG0("Exit PortModel data 6\n"); - return QVariant(); - } - } + return QString("Port %1: %2 [%3] (%4)"). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->id()). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->name()). + arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->description()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 5\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); + switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) + { + case OstProto::LinkStateUnknown: + return QIcon(":/icons/bullet_white.png"); + case OstProto::LinkStateDown: + return QIcon(":/icons/bullet_red.png"); + case OstProto::LinkStateUp: + return QIcon(":/icons/bullet_green.png"); + default: + qFatal("unexpected/unimplemented port oper state"); + } + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } - return QVariant(); + return QVariant(); } QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) - return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); - if (orientation == Qt::Horizontal) - return QVariant(); - else - return QString("Name"); + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); } QModelIndex PortModel::index (int row, int col, - const QModelIndex & parent) const + const QModelIndex & parent) const { - if (!hasIndex(row, col, parent)) - return QModelIndex(); - - //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", - // row, col, parent.row(), parent.column(), parent.internalId()); + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); - if (!parent.isValid()) - { - // Top Level Item - quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; - quint32 id = (pg << 16) | p; - //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - return createIndex(row, col, id); - } - else - { - quint16 pg = parent.internalId() >> 16; - quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); - quint32 id = (pg << 16) | p; - //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - return createIndex(row, col, id); - } + return createIndex(row, col, id); + } } QModelIndex PortModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return QModelIndex(); - - //qDebug("parent: R=%d, C=%d ID=%llx\n", - // index.row(), index.column(), index.internalId()); + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); - quint16 pg = index.internalId() >> 16; - quint16 p = index.internalId() & 0x0000FFFF; + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; - //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); - if (p == 0xFFFF) - { - //qDebug("parent ret: NULL\n"); - // Top Level Item - PG - return QModelIndex(); - } + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } - quint32 id = (pg << 16) | 0xFFFF; - //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); - return createIndex(pgl->indexOfPortGroup(pg), 0, id); + return createIndex(pgl->indexOfPortGroup(pg), 0, id); } bool PortModel::isPortGroup(const QModelIndex& index) { - if ((index.internalId() & 0xFFFF) == 0xFFFF) - return true; - else - return false; + if ((index.internalId() & 0xFFFF) == 0xFFFF) + return true; + else + return false; } bool PortModel::isPort(const QModelIndex& index) { - if ((index.internalId() & 0xFFFF) != 0xFFFF) - return true; - else - return false; + if ((index.internalId() & 0xFFFF) != 0xFFFF) + return true; + else + return false; } quint32 PortModel::portGroupId(const QModelIndex& index) { - return (index.internalId()) >> 16 & 0xFFFF; + return (index.internalId()) >> 16 & 0xFFFF; } quint32 PortModel::portId(const QModelIndex& index) { - return (index.internalId()) & 0xFFFF; + return (index.internalId()) & 0xFFFF; } @@ -266,46 +266,46 @@ quint32 PortModel::portId(const QModelIndex& index) // ---------------------------------------------- void PortModel::when_portGroupDataChanged(int portGroupId, int portId) { - QModelIndex index; - int row; + QModelIndex index; + int row; - if (portId == 0xFFFF) - row = pgl->indexOfPortGroup(portGroupId); - else - row = portId; + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; - index = createIndex(row, 0, (portGroupId << 16) | portId); + index = createIndex(row, 0, (portGroupId << 16) | portId); - emit dataChanged(index, index); + emit dataChanged(index, index); } void PortModel::portGroupAboutToBeAppended() { - int row; + int row; - row = pgl->mPortGroups.size(); - beginInsertRows(QModelIndex(), row, row); + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); } void PortModel::portGroupAppended() { - endInsertRows(); + endInsertRows(); } void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) { - int row; + int row; - row = pgl->mPortGroups.indexOf(portGroup); - beginRemoveRows(QModelIndex(), row, row); + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); } void PortModel::portGroupRemoved() { - endRemoveRows(); + endRemoveRows(); } void PortModel::when_portListChanged() { - reset(); + reset(); } diff --git a/client/portmodel.h b/client/portmodel.h index bd4b791..eea9763 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -8,42 +8,42 @@ class PortGroup; class PortModel : public QAbstractItemModel { - Q_OBJECT + Q_OBJECT - friend class PortGroupList; + friend class PortGroupList; - PortGroupList *pgl; + PortGroupList *pgl; - public: - PortModel(PortGroupList *p, QObject *parent = 0); + public: + PortModel(PortGroupList *p, QObject *parent = 0); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; - bool isPortGroup(const QModelIndex& index); - bool isPort(const QModelIndex& index); - quint32 portGroupId(const QModelIndex& index); - quint32 portId(const QModelIndex& index); + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); private slots: - void when_portGroupDataChanged(int portGroupId, int portId); + void when_portGroupDataChanged(int portGroupId, int portId); - void portGroupAboutToBeAppended(); - void portGroupAppended(); - void portGroupAboutToBeRemoved(PortGroup *portGroup); - void portGroupRemoved(); + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); - void when_portListChanged(); + void when_portListChanged(); #if 0 - void triggerLayoutAboutToBeChanged(); - void triggerLayoutChanged(); + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); #endif }; diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 6c97d85..b21bd37 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -2,110 +2,110 @@ PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) { - setupUi(this); + setupUi(this); - mUnselected.setSortRole(PositionRole); + mUnselected.setSortRole(PositionRole); - lvUnselected->setModel(&mUnselected); - lvSelected->setModel(&mSelected); + lvUnselected->setModel(&mUnselected); + lvSelected->setModel(&mSelected); } QList PortStatsFilterDialog::getItemList(bool* ok, - QAbstractItemModel *model, Qt::Orientation orientation, - QList initial) + QAbstractItemModel *model, Qt::Orientation orientation, + QList initial) { - QList ret; + QList ret; - uint count = (orientation == Qt::Vertical) ? - model->rowCount() : model->columnCount(); + uint count = (orientation == Qt::Vertical) ? + model->rowCount() : model->columnCount(); - *ok = false; + *ok = false; - mUnselected.clear(); - mSelected.clear(); + 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); - item->setFlags(Qt::ItemIsSelectable - | Qt::ItemIsDragEnabled - //| Qt::ItemIsDropEnabled - | Qt::ItemIsEnabled); + for (uint i = 0; i < count; i++) + { + QStandardItem *item; + + item = new QStandardItem(model->headerData(i, orientation).toString()); + item->setData(i, PositionRole); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); - if (initial.contains(i)) - mSelected.appendRow(item); - else - mUnselected.appendRow(item); - } + if (initial.contains(i)) + mSelected.appendRow(item); + else + mUnselected.appendRow(item); + } - // No need to sort right now 'coz we have inserted items in order + // 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; - } + 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; + return ret; } void PortStatsFilterDialog::on_tbSelectIn_clicked() { - QStandardItem *item; - while (lvUnselected->selectionModel()->selectedIndexes().size()) - { - item = mUnselected.takeItem(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mUnselected.removeRow(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row())) - mSelected.appendRow(item); - } + QStandardItem *item; + while (lvUnselected->selectionModel()->selectedIndexes().size()) + { + item = mUnselected.takeItem(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mUnselected.removeRow(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row())) + mSelected.appendRow(item); + } } void PortStatsFilterDialog::on_tbSelectOut_clicked() { - QStandardItem *item; + QStandardItem *item; - while (lvSelected->selectionModel()->selectedIndexes().size()) - { - item = mSelected.takeItem(lvSelected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mSelected.removeRow(lvSelected->selectionModel()-> - selectedIndexes().at(0).row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } - } + while (lvSelected->selectionModel()->selectedIndexes().size()) + { + item = mSelected.takeItem(lvSelected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mSelected.removeRow(lvSelected->selectionModel()-> + selectedIndexes().at(0).row())) + { + mUnselected.appendRow(item); + mUnselected.sort(0); + } + } } void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) { - QStandardItem *item; + QStandardItem *item; - item = mUnselected.takeItem(lvUnselected->currentIndex().row()); - if (mUnselected.removeRow(lvUnselected->currentIndex().row())) - mSelected.appendRow(item); + item = mUnselected.takeItem(lvUnselected->currentIndex().row()); + if (mUnselected.removeRow(lvUnselected->currentIndex().row())) + mSelected.appendRow(item); } void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) { - QStandardItem *item; + QStandardItem *item; - item = mSelected.takeItem(lvSelected->currentIndex().row()); - if (mSelected.removeRow(lvSelected->currentIndex().row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } + 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 cabbf0c..ce8016c 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -9,26 +9,26 @@ class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog { - Q_OBJECT + Q_OBJECT public: - PortStatsFilterDialog(QWidget *parent = 0); - QList getItemList(bool* ok, QAbstractItemModel *model, - Qt::Orientation orientation = Qt::Vertical, - QList initial = QList()); + 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; + enum ItemRole { + PositionRole = Qt::UserRole + 1 + }; + QStandardItemModel mUnselected; + QStandardItemModel mSelected; private slots: - void on_tbSelectIn_clicked(); - void on_tbSelectOut_clicked(); - void on_lvUnselected_doubleClicked(const QModelIndex &index); - void on_lvSelected_doubleClicked(const QModelIndex &index); + void on_tbSelectIn_clicked(); + void on_tbSelectOut_clicked(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); }; #endif diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index ce75846..5aec2b6 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -4,241 +4,241 @@ #include PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) - : QAbstractTableModel(parent) + : QAbstractTableModel(parent) { - QTimer *timer; + QTimer *timer; - pgl = p; + pgl = p; - timer = new QTimer(); - connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); - timer->start(1000); + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); } int PortStatsModel::rowCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; + if (parent.isValid()) + return 0; - if (numPorts.isEmpty()) - return 0; + if (numPorts.isEmpty()) + return 0; - if (numPorts.last() == 0) - return 0; + if (numPorts.last() == 0) + return 0; - return (int) e_STAT_MAX; + return (int) e_STAT_MAX; } int PortStatsModel::columnCount(const QModelIndex &parent ) const { - if (parent.isValid()) - return 0; - else - if (numPorts.isEmpty()) - return 0; - else - return numPorts.last(); + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); } void PortStatsModel::getDomainIndexes(const QModelIndex &index, - uint &portGroupIdx, uint &portIdx) const + uint &portGroupIdx, uint &portIdx) const { - int portNum; + int portNum; - // TODO(LOW): Optimize using binary search: see qLowerBound() - portNum = index.column() + 1; - for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) - if (portNum <= numPorts.at(portGroupIdx)) - break; + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; - if (portGroupIdx) - { - if (numPorts.at(portGroupIdx -1)) - portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); - else - portIdx = portNum - 1; - } - else - portIdx = portNum - 1; + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; - //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); } QVariant PortStatsModel::data(const QModelIndex &index, int role) const { - uint pgidx, pidx; - int row; + uint pgidx, pidx; + int row; - // Check for a valid index - if (!index.isValid()) - return QVariant(); + // Check for a valid index + if (!index.isValid()) + return QVariant(); - // Check for row/column limits - row = index.row(); - if (row >= e_STAT_MAX) - return QVariant(); + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); - if (numPorts.isEmpty()) - return QVariant(); + if (numPorts.isEmpty()) + return QVariant(); - if (index.column() >= (numPorts.last())) - return QVariant(); + if (index.column() >= (numPorts.last())) + return QVariant(); - getDomainIndexes(index, pgidx, pidx); + getDomainIndexes(index, pgidx, pidx); - // Check role - if (role == Qt::DisplayRole) - { - OstProto::PortStats stats; + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; - stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); - switch(row) - { - // States - case e_LINK_STATE: - return LinkStateName.at(stats.state().link_state()); + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); - case e_TRANSMIT_STATE: - return BoolStateName.at(stats.state().is_transmit_on()); + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); - case e_CAPTURE_STATE: - return BoolStateName.at(stats.state().is_capture_on()); + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); - // Statistics - case e_STAT_FRAMES_RCVD: - return stats.rx_pkts(); + // Statistics + case e_STAT_FRAMES_RCVD: + return stats.rx_pkts(); - case e_STAT_FRAMES_SENT: - return stats.tx_pkts(); + case e_STAT_FRAMES_SENT: + return stats.tx_pkts(); - case e_STAT_FRAME_SEND_RATE: - return stats.tx_pps(); + case e_STAT_FRAME_SEND_RATE: + return stats.tx_pps(); - case e_STAT_FRAME_RECV_RATE: - return stats.rx_pps(); + case e_STAT_FRAME_RECV_RATE: + return stats.rx_pps(); - case e_STAT_BYTES_RCVD: - return stats.rx_bytes(); + case e_STAT_BYTES_RCVD: + return stats.rx_bytes(); - case e_STAT_BYTES_SENT: - return stats.tx_bytes(); + case e_STAT_BYTES_SENT: + return stats.tx_bytes(); - case e_STAT_BYTE_SEND_RATE: - return stats.tx_bps(); + case e_STAT_BYTE_SEND_RATE: + return stats.tx_bps(); - case e_STAT_BYTE_RECV_RATE: - return stats.rx_bps(); + case e_STAT_BYTE_RECV_RATE: + return stats.rx_bps(); #if 0 - case e_STAT_FRAMES_RCVD_NIC: - return stats.rx_pkts_nic(); + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); - case e_STAT_FRAMES_SENT_NIC: - return stats.tx_pkts_nic(); + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); - case e_STAT_BYTES_RCVD_NIC: - return stats.rx_bytes_nic(); + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); - case e_STAT_BYTES_SENT_NIC: - return stats.tx_bytes_nic(); + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); #endif - default: - qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, - index.row()); - return 0; - } - } - else if (role == Qt::TextAlignmentRole) - { - if (row >= e_STATE_START && row <= e_STATE_END) - return Qt::AlignHCenter; - else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) - return Qt::AlignRight; - else - return QVariant(); - } - else - return QVariant(); + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); } QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const { #ifdef Q_OS_WIN32 - // TODO(MED): The limitations should be the server's not the client's! - // Ideally we shd enhance the protocol to convey limitation(s), if any, - // from server to client - if (role == Qt::ToolTipRole) - { - if (orientation == Qt::Horizontal) - { - return QString("Limitation(s)" - "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" - "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" - "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" - ); - } - else - return QVariant(); - } + // TODO(MED): The limitations should be the server's not the client's! + // Ideally we shd enhance the protocol to convey limitation(s), if any, + // from server to client + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + return QString("Limitation(s)" + "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" + "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" + "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" + ); + } + else + return QVariant(); + } #endif - if (role != Qt::DisplayRole) - return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); - if (orientation == Qt::Horizontal) - { - uint portGroupIdx, portIdx; + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; - getDomainIndexes(index(0, section), portGroupIdx, portIdx); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); #ifdef Q_OS_WIN32 - return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); + return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); #else - return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); #endif - } - else - return PortStatName.at(section); + } + else + return PortStatName.at(section); } void PortStatsModel::portListFromIndex(QModelIndexList indices, - QList &portList) + QList &portList) { - int i, j; - QModelIndexList selectedCols(indices); + int i, j; + QModelIndexList selectedCols(indices); - portList.clear(); + portList.clear(); - //selectedCols = indices.selectedColumns(); - for (i = 0; i < selectedCols.size(); i++) - { - uint portGroupIdx, portIdx; + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; - getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); - for (j = 0; j < portList.size(); j++) - { - if (portList[j].portGroupId == portGroupIdx) - break; - } + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } - if (j >= portList.size()) - { - // PortGroup Not found - PortGroupAndPortList p; + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; - p.portGroupId = portGroupIdx; - p.portList.append(portIdx); + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); - portList.append(p); - } - else - { - // PortGroup found + portList.append(p); + } + else + { + // PortGroup found - portList[j].portList.append(portIdx); - } - } + portList[j].portList.append(portIdx); + } + } } // @@ -246,43 +246,43 @@ void PortStatsModel::portListFromIndex(QModelIndexList indices, // void PortStatsModel::when_portListChanged() { - int i, count = 0; + int i, count = 0; - // recalc numPorts - while (numPorts.size()) - numPorts.removeFirst(); + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); - for (i = 0; i < pgl->mPortGroups.size(); i++) - { - count += pgl->mPortGroups.at(i)->numPorts(); - numPorts.append(count); - } + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } - reset(); + reset(); } void PortStatsModel::on_portStatsUpdate(int port, void*stats) { - QModelIndex topLeft = index(port, 0, QModelIndex()); - QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); - emit dataChanged(topLeft, bottomRight); + emit dataChanged(topLeft, bottomRight); } void PortStatsModel::updateStats() { - // Request each portgroup to fetch updated stats - the port group - // raises a signal once updated stats are available - for (int i = 0; i < pgl->mPortGroups.size(); i++) - pgl->mPortGroups[i]->getPortStats(); + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); } void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) { - // FIXME(MED): update only the changed ports, not all + // FIXME(MED): update only the changed ports, not all - QModelIndex topLeft = index(0, 0, QModelIndex()); - QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); - emit dataChanged(topLeft, bottomRight); + emit dataChanged(topLeft, bottomRight); } diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 02f02dd..e253e22 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -5,112 +5,112 @@ #include typedef enum { - // State - e_STATE_START = 0, + // State + e_STATE_START = 0, - e_LINK_STATE = e_STATE_START, - e_TRANSMIT_STATE, - e_CAPTURE_STATE, + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, - e_STATE_END = e_CAPTURE_STATE, + e_STATE_END = e_CAPTURE_STATE, - // Statistics - e_STATISTICS_START, + // Statistics + e_STATISTICS_START, - e_STAT_FRAMES_RCVD = e_STATISTICS_START, - e_STAT_FRAMES_SENT, - e_STAT_FRAME_SEND_RATE, - e_STAT_FRAME_RECV_RATE, - e_STAT_BYTES_RCVD, - e_STAT_BYTES_SENT, - e_STAT_BYTE_SEND_RATE, - e_STAT_BYTE_RECV_RATE, + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, #if 0 - e_STAT_FRAMES_RCVD_NIC, - e_STAT_FRAMES_SENT_NIC, - e_STAT_BYTES_RCVD_NIC, - e_STAT_BYTES_SENT_NIC, + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, #endif - e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, - e_STAT_MAX + e_STAT_MAX } PortStat; static QStringList PortStatName = (QStringList() - << "Link State" - << "Transmit State" - << "Capture State" + << "Link State" + << "Transmit State" + << "Capture State" - << "Frames Received" - << "Frames Sent" - << "Frame Send Rate (fps)" - << "Frame Receive Rate (fps)" - << "Bytes Received" - << "Bytes Sent" - << "Byte Send Rate (Bps)" - << "Byte Receive Rate (Bps)" + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" #if 0 - << "Frames Received (NIC)" - << "Frames Sent (NIC)" - << "Bytes Received (NIC)" - << "Bytes Sent (NIC)" + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" #endif ); static QStringList LinkStateName = (QStringList() - << "Unknown" - << "Down" - << "Up" + << "Unknown" + << "Down" + << "Up" ); static QStringList BoolStateName = (QStringList() - << "Off" - << "On" + << "Off" + << "On" ); class PortGroupList; class PortStatsModel : public QAbstractTableModel { - Q_OBJECT + Q_OBJECT - public: + public: - PortStatsModel(PortGroupList *p, QObject *parent = 0); + PortStatsModel(PortGroupList *p, 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; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; - class PortGroupAndPortList { - public: - uint portGroupId; - QList portList; - }; - void portListFromIndex(QModelIndexList indices, - QList &portList); + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); - public slots: - void when_portListChanged(); - void on_portStatsUpdate(int port, void*stats); - void when_portGroup_stats_update(quint32 portGroupId); + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); - private slots: - void updateStats(); + private slots: + void updateStats(); - private: - PortGroupList *pgl; + private: + PortGroupList *pgl; - // numPorts stores the num of ports per portgroup - // in the same order as the portgroups are index in the pgl - // Also it stores them as cumulative totals - QList numPorts; + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; - void getDomainIndexes(const QModelIndex &index, - uint &portGroupIdx, uint &portIdx) const; + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; }; diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 26e2b10..c10834d 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -6,17 +6,17 @@ #include "QHeaderView" PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); - this->pgl = pgl; - model = pgl->getPortStatsModel(); - tvPortStats->setModel(model); + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); - tvPortStats->verticalHeader()->setHighlightSections(false); - tvPortStats->verticalHeader()->setDefaultSectionSize( - tvPortStats->verticalHeader()->minimumSectionSize()); + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); } @@ -28,134 +28,134 @@ PortStatsWindow::~PortStatsWindow() void PortStatsWindow::on_tbStartTransmit_clicked() { - QList pgpl; + QList pgpl; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - startTx(&pgpl[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } } void PortStatsWindow::on_tbStopTransmit_clicked() { - QList pgpl; + QList pgpl; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - stopTx(&pgpl[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } } void PortStatsWindow::on_tbStartCapture_clicked() { - // TODO(MED) - QList pgpl; + // TODO(MED) + QList pgpl; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - startCapture(&pgpl[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbStopCapture_clicked() { - // TODO(MED) - QList pgpl; + // TODO(MED) + QList pgpl; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - stopCapture(&pgpl[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbViewCapture_clicked() { - // TODO(MED) - QList pgpl; + // TODO(MED) + QList pgpl; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - viewCapture(&pgpl[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } } void PortStatsWindow::on_tbClear_clicked() { - QList portList; + QList portList; - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - portList); + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < portList.size(); i++) - { - pgl->portGroupByIndex(portList.at(i).portGroupId). - clearPortStats(&portList[i].portList); - } + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } } void PortStatsWindow::on_tbClearAll_clicked() { - for (int i = 0; i < pgl->numPortGroups(); i++) - { - pgl->portGroupByIndex(0).clearPortStats(); - } + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } } void PortStatsWindow::on_tbFilter_clicked() { - bool ok; - QList currentColumns, newColumns; - PortStatsFilterDialog dialog; + bool ok; + QList currentColumns, newColumns; + PortStatsFilterDialog dialog; - for(int i = 0; i < model->columnCount(); i++) - if (!tvPortStats->isColumnHidden(i)) - currentColumns.append(i); + for(int i = 0; i < model->columnCount(); i++) + if (!tvPortStats->isColumnHidden(i)) + currentColumns.append(i); - newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); + newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); - if (ok) - { - // hide/show sections first ... - for(int i = 0; i < model->columnCount(); i++) - tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + if (ok) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); - // ... then for the 'shown' columns, set the visual index - for(int i = 0; i < newColumns.size(); i++) - { - tvPortStats->horizontalHeader()->moveSection(tvPortStats-> - horizontalHeader()->visualIndex(newColumns.at(i)), i); - } - } + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } } diff --git a/client/portstatswindow.h b/client/portstatswindow.h index c1d581f..a390446 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -9,28 +9,28 @@ class PortStatsWindow : public QWidget, public Ui::PortStatsWindow { - Q_OBJECT + Q_OBJECT public: - PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); - ~PortStatsWindow(); + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); private: - PortGroupList *pgl; - PortStatsModel *model; + PortGroupList *pgl; + PortStatsModel *model; private slots: - void on_tbStartTransmit_clicked(); - void on_tbStopTransmit_clicked(); + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); - void on_tbStartCapture_clicked(); - void on_tbStopCapture_clicked(); - void on_tbViewCapture_clicked(); + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); - void on_tbClear_clicked(); - void on_tbClearAll_clicked(); + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); - void on_tbFilter_clicked(); + void on_tbFilter_clicked(); }; #endif diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 54d4b8b..23684db 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -6,67 +6,67 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) { - StreamListDelegate *delegate = new StreamListDelegate; - //slm = new StreamListModel(); - //plm = new PortGroupList(); - plm = pgl; + StreamListDelegate *delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; - setupUi(this); + setupUi(this); - tvStreamList->setItemDelegate(delegate); + tvStreamList->setItemDelegate(delegate); - tvStreamList->verticalHeader()->setDefaultSectionSize( - tvStreamList->verticalHeader()->minimumSectionSize()); + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); - // Populate Context Menu Actions - tvPortList->addAction(actionNew_Port_Group); - tvPortList->addAction(actionDelete_Port_Group); - tvPortList->addAction(actionConnect_Port_Group); - tvPortList->addAction(actionDisconnect_Port_Group); + // Populate Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); - tvStreamList->addAction(actionNew_Stream); - tvStreamList->addAction(actionEdit_Stream); - tvStreamList->addAction(actionDelete_Stream); + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); - tvStreamList->setModel(plm->getStreamModel()); - tvPortList->setModel(plm->getPortModel()); - - connect( plm->getPortModel(), - SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_portModel_dataChanged(const QModelIndex&, - const QModelIndex&))); + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); - connect(plm->getPortModel(), SIGNAL(modelReset()), - SLOT(when_portModel_reset())); + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); - connect( tvPortList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_portView_currentChanged(const QModelIndex&, - const QModelIndex&))); + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); - connect( tvStreamList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_streamView_currentChanged(const QModelIndex&, - const QModelIndex&))); - connect( tvStreamList->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(when_streamView_selectionChanged())); + connect( tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_streamView_currentChanged(const QModelIndex&, + const QModelIndex&))); + connect( tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_streamView_selectionChanged())); #if 0 - connect( tvPortList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); #endif - tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); - tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); - // Initially we don't have any ports/streams - so send signal triggers - when_portView_currentChanged(QModelIndex(), QModelIndex()); - when_streamView_currentChanged(QModelIndex(), QModelIndex()); + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + when_streamView_currentChanged(QModelIndex(), QModelIndex()); - //! \todo Hide the Aggregate Box till we add support - frAggregate->setHidden(true); + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); } PortsWindow::~PortsWindow() @@ -75,342 +75,342 @@ PortsWindow::~PortsWindow() void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) { - StreamConfigDialog *scd; + StreamConfigDialog *scd; - if (!index.isValid()) - { - qDebug("%s: invalid index", __FUNCTION__); - return; - } - scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), - index.row(), this); - qDebug("stream list activated\n"); - scd->exec(); // TODO: chk retval - delete scd; + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; } void PortsWindow::when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) + const QModelIndex& previous) { - plm->getStreamModel()->setCurrentPortIndex(current); - updatePortViewActions(current); - updateStreamViewActions(); + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); - if (!current.isValid()) - { - qDebug("setting stacked widget to blank page"); - swDetail->setCurrentIndex(2); // blank page - } - else - { - if (plm->isPortGroup(current)) - { - swDetail->setCurrentIndex(1); // portGroup detail page - } - else if (plm->isPort(current)) - { - swDetail->setCurrentIndex(0); // port detail page - } - } + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } } void PortsWindow::when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) + const QModelIndex& previous) { - qDebug("stream view current changed"); - updateStreamViewActions(); + qDebug("stream view current changed"); + updateStreamViewActions(); } void PortsWindow::when_streamView_selectionChanged() { - qDebug("stream view selection changed"); - updateStreamViewActions(); + qDebug("stream view selection changed"); + updateStreamViewActions(); } void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, - const QModelIndex& bottomRight) + const QModelIndex& bottomRight) { #if 0 // not sure why the >= <= operators are not overloaded in QModelIndex - if ((tvPortList->currentIndex() >= topLeft) && - (tvPortList->currentIndex() <= bottomRight)) + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) #endif - if ((topLeft < tvPortList->currentIndex()) || - (topLeft == tvPortList->currentIndex()) && - ((tvPortList->currentIndex() < bottomRight)) || - (tvPortList->currentIndex() == bottomRight)) - { - updatePortViewActions(tvPortList->currentIndex()); - } + if ((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex()) && + ((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight)) + { + updatePortViewActions(tvPortList->currentIndex()); + } } void PortsWindow::when_portModel_reset() { - when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } #if 0 void PortsWindow::updateStreamViewActions(const QModelIndex& current) { - if (current.isValid()) - actionDelete_Stream->setEnabled(true); - else - actionDelete_Stream->setDisabled(true); + if (current.isValid()) + actionDelete_Stream->setEnabled(true); + else + actionDelete_Stream->setDisabled(true); } #endif void PortsWindow::updateStreamViewActions() { - // For some reason hasSelection() returns true even if selection size is 0 - // so additional check for size introduced - if (tvStreamList->selectionModel()->hasSelection() && - (tvStreamList->selectionModel()->selection().size() > 0)) - { - qDebug("Has selection %d", - tvStreamList->selectionModel()->selection().size()); + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); - // If more than one non-contiguous ranges selected, - // disable "New" and "Edit" - if (tvStreamList->selectionModel()->selection().size() > 1) - { - actionNew_Stream->setDisabled(true); - actionEdit_Stream->setDisabled(true); - } - else - { - actionNew_Stream->setEnabled(true); + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); - // Enable "Edit" only if the single range has a single row - if (tvStreamList->selectionModel()->selection().at(0).height() > 1) - actionEdit_Stream->setDisabled(true); - else - actionEdit_Stream->setEnabled(true); - } + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } - // Delete is always enabled as long as we have a selection - actionDelete_Stream->setEnabled(true); - } - else - { - qDebug("No selection"); - actionNew_Stream->setEnabled(true); - actionEdit_Stream->setDisabled(true); - actionDelete_Stream->setDisabled(true); - } + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + actionNew_Stream->setEnabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } } void PortsWindow::updatePortViewActions(const QModelIndex& current) { - if (!current.isValid()) - { - qDebug("current is now invalid"); - actionDelete_Port_Group->setDisabled(true); - actionConnect_Port_Group->setDisabled(true); - actionDisconnect_Port_Group->setDisabled(true); - - goto _EXIT; - } + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + goto _EXIT; + } - qDebug("currentChanged %llx", current.internalId()); + qDebug("currentChanged %llx", current.internalId()); - if (plm->isPortGroup(current)) - { - actionDelete_Port_Group->setEnabled(true); - switch(plm->portGroup(current).state()) - { - case QAbstractSocket::UnconnectedState: - case QAbstractSocket::ClosingState: - qDebug("state = unconnected|closing"); - actionConnect_Port_Group->setEnabled(true); - actionDisconnect_Port_Group->setDisabled(true); - break; + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; - case QAbstractSocket::HostLookupState: - case QAbstractSocket::ConnectingState: - case QAbstractSocket::ConnectedState: - qDebug("state = lookup|connecting|connected"); - actionConnect_Port_Group->setDisabled(true); - actionDisconnect_Port_Group->setEnabled(true); - break; + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; - case QAbstractSocket::BoundState: - case QAbstractSocket::ListeningState: - default: - // FIXME(LOW): indicate error - qDebug("unexpected state"); - break; - } - } - else if (plm->isPort(current)) - { - actionDelete_Port_Group->setEnabled(false); - actionConnect_Port_Group->setEnabled(false); - actionDisconnect_Port_Group->setEnabled(false); - } + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setEnabled(false); + actionConnect_Port_Group->setEnabled(false); + actionDisconnect_Port_Group->setEnabled(false); + } _EXIT: - return; + return; } void PortsWindow::on_pbApply_clicked() { - QModelIndex curPort; - QModelIndex curPortGroup; + QModelIndex curPort; + QModelIndex curPortGroup; - curPort = tvPortList->selectionModel()->currentIndex(); - if (!curPort.isValid()) - { - qDebug("%s: curPort is invalid", __FUNCTION__); - goto _exit; - } + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } - if (!plm->isPort(curPort)) - { - qDebug("%s: curPort is not a port", __FUNCTION__); - goto _exit; - } + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } - curPortGroup = plm->getPortModel()->parent(curPort); - if (!curPortGroup.isValid()) - { - qDebug("%s: curPortGroup is invalid", __FUNCTION__); - goto _exit; - } - if (!plm->isPortGroup(curPortGroup)) - { - qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); - goto _exit; - } + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } - // FIXME(HI): shd this be a signal? - //portGroup.when_configApply(port); - // FIXME(MED): mixing port id and index!!! - plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); _exit: - return; + return; #if 0 - // TODO (LOW): This block is for testing only - QModelIndex current = tvPortList->selectionModel()->currentIndex(); + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); - if (current.isValid()) - qDebug("current = %llx", current.internalId()); - else - qDebug("current is invalid"); + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); #endif } void PortsWindow::on_actionNew_Port_Group_triggered() { - bool ok; - QString text = QInputDialog::getText(this, - "Add Port Group", "Port Group Address (IP[:Port])", - QLineEdit::Normal, lastNewPortGroup, &ok); - - if (ok) - { - QStringList addr = text.split(":"); - if (addr.size() == 1) // Port unspecified - addr.append(QString().setNum(DEFAULT_SERVER_PORT)); - PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); - plm->addPortGroup(*pg); - lastNewPortGroup = text; - } + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } } void PortsWindow::on_actionDelete_Port_Group_triggered() { - QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QModelIndex current = tvPortList->selectionModel()->currentIndex(); - if (current.isValid()) - plm->removePortGroup(plm->portGroup(current)); + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); } void PortsWindow::on_actionConnect_Port_Group_triggered() { - QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QModelIndex current = tvPortList->selectionModel()->currentIndex(); - if (current.isValid()) - plm->portGroup(current).connectToHost(); + if (current.isValid()) + plm->portGroup(current).connectToHost(); } void PortsWindow::on_actionDisconnect_Port_Group_triggered() { - QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QModelIndex current = tvPortList->selectionModel()->currentIndex(); - if (current.isValid()) - plm->portGroup(current).disconnectFromHost(); + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); } #if 0 void PortsWindow::on_actionNew_Stream_triggered() { - qDebug("New Stream Action"); + qDebug("New Stream Action"); - int row = 0; + int row = 0; - if (tvStreamList->currentIndex().isValid()) - row = tvStreamList->currentIndex().row(); - plm->getStreamModel()->insertRows(row, 1); + if (tvStreamList->currentIndex().isValid()) + row = tvStreamList->currentIndex().row(); + plm->getStreamModel()->insertRows(row, 1); } void PortsWindow::on_actionDelete_Stream_triggered() { - qDebug("Delete Stream Action"); - if (tvStreamList->currentIndex().isValid()) - plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); + qDebug("Delete Stream Action"); + if (tvStreamList->currentIndex().isValid()) + plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); } #endif void PortsWindow::on_actionNew_Stream_triggered() { - qDebug("New Stream Action"); + qDebug("New Stream Action"); - // In case nothing is selected, insert 1 row at the top - int row = 0, count = 1; + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; - // In case we have a single range selected; insert as many rows as - // in the singe selected range before the top of the selected range - if (tvStreamList->selectionModel()->selection().size() == 1) - { - row = tvStreamList->selectionModel()->selection().at(0).top(); - count = tvStreamList->selectionModel()->selection().at(0).height(); - } + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } - plm->getStreamModel()->insertRows(row, count); + plm->getStreamModel()->insertRows(row, count); } void PortsWindow::on_actionEdit_Stream_triggered() { - qDebug("Edit Stream Action"); + qDebug("Edit Stream Action"); - // Ensure we have only one range selected which contains only one row - if ((tvStreamList->selectionModel()->selection().size() == 1) && - (tvStreamList->selectionModel()->selection().at(0).height() == 1)) - { - on_tvStreamList_activated(tvStreamList->selectionModel()-> - selection().at(0).topLeft()); - } + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } } void PortsWindow::on_actionDelete_Stream_triggered() { - qDebug("Delete Stream Action"); + qDebug("Delete Stream Action"); - QModelIndex index; + QModelIndex index; - if (tvStreamList->selectionModel()->hasSelection()) - { - qDebug("SelectedIndexes %d", - tvStreamList->selectionModel()->selectedRows().size()); - while(tvStreamList->selectionModel()->selectedRows().size()) - { - index = tvStreamList->selectionModel()->selectedRows().at(0); - plm->getStreamModel()->removeRows(index.row(), 1); - } - } - else - qDebug("No selection"); + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); } diff --git a/client/portswindow.h b/client/portswindow.h index b291f91..aec7216 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -15,43 +15,43 @@ LOW class PortsWindow : public QWidget, private Ui::PortsWindow { - Q_OBJECT + Q_OBJECT - //QAbstractItemModel *slm; // stream list model - PortGroupList *plm; + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; public: - PortsWindow(PortGroupList *pgl, QWidget *parent = 0); - ~PortsWindow(); + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); private: - QString lastNewPortGroup; + QString lastNewPortGroup; - void updatePortViewActions(const QModelIndex& current); - //void updateStreamViewActions(const QModelIndex& current); - void updateStreamViewActions(); + void updatePortViewActions(const QModelIndex& current); + //void updateStreamViewActions(const QModelIndex& current); + void updateStreamViewActions(); private slots: - void on_tvStreamList_activated(const QModelIndex & index); - void when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& previous); - void when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous); - void when_streamView_selectionChanged(); - void when_portModel_dataChanged(const QModelIndex& topLeft, - const QModelIndex& bottomRight); - void when_portModel_reset(); + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_selectionChanged(); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); - void on_pbApply_clicked(); + void on_pbApply_clicked(); - void on_actionNew_Port_Group_triggered(); - void on_actionDelete_Port_Group_triggered(); - void on_actionConnect_Port_Group_triggered(); - void on_actionDisconnect_Port_Group_triggered(); + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); - void on_actionNew_Stream_triggered(); - void on_actionEdit_Stream_triggered(); - void on_actionDelete_Stream_triggered(); + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); }; #endif diff --git a/client/stream.cpp b/client/stream.cpp index d2791e3..049d554 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -8,8 +8,8 @@ Stream::Stream() { - //mId = 0xFFFFFFFF; - setEnabled(true); + //mId = 0xFFFFFFFF; + setEnabled(true); } Stream::~Stream() @@ -19,42 +19,42 @@ Stream::~Stream() void Stream::loadProtocolWidgets() { #if 0 - //protocols.loadConfigWidgets(); - foreach(AbstractProtocol* proto, *currentFrameProtocols) - { - proto->loadConfigWidget(); - } + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } #else - ProtocolListIterator *iter; + ProtocolListIterator *iter; - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->loadConfigWidget(); - } - delete iter; + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; #endif } void Stream::storeProtocolWidgets() { #if 0 - //protocols.storeConfigWidgets(); - foreach(const AbstractProtocol* proto, frameProtocol()) - { - proto->storeConfigWidget(); - _iter->toFront(); - } + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } #else - ProtocolListIterator *iter; + ProtocolListIterator *iter; - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->storeConfigWidget(); - } - delete iter; + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; #endif } diff --git a/client/stream.h b/client/stream.h index 1539af3..1b4e756 100644 --- a/client/stream.h +++ b/client/stream.h @@ -10,14 +10,14 @@ class Stream : public StreamBase { - //quint32 mId; + //quint32 mId; public: - Stream(); - ~Stream(); + Stream(); + ~Stream(); - void loadProtocolWidgets(); - void storeProtocolWidgets(); + void loadProtocolWidgets(); + void storeProtocolWidgets(); }; #endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 5201099..c6a950d 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -14,538 +14,538 @@ extern ProtocolManager OstProtocolManager; int StreamConfigDialog::lastTopLevelTabIndex = 0; StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, - QWidget *parent) : QDialog (parent), mPort(port) + QWidget *parent) : QDialog (parent), mPort(port) { - OstProto::Stream s; - mCurrentStreamIndex = streamIndex; - mpStream = new Stream; - mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); - mpStream->protoDataCopyFrom(s); - _iter = mpStream->createProtocolListIterator(); - isUpdateInProgress = false; + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; - setupUi(this); - setupUiExtra(); + setupUi(this); + setupUiExtra(); - for (int i = ProtoMin; i < ProtoMax; i++) - { - bgProto[i]->setProperty("ProtocolLevel", i); - bgProto[i]->setProperty("ProtocolId", ButtonIdNone); - connect(bgProto[i], SIGNAL(buttonClicked(int)), - this, SLOT(updateProtocol(int))); - } + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } - //! \todo causes a crash! -#if 0 - connect(lePktLen, SIGNAL(textEdited(QString)), - this, SLOT(updateContents())); + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); #endif - // Time to play match the signals and slots! + // Time to play match the signals and slots! - // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None - connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and - // disable the subsequent protocol group as well - connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); - connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); - connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); - connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); - connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); - connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); - connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); - connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); - // Setup valid subsequent protocols for L2 and L3 protocols - for (int i = ProtoL2; i <= ProtoL3; i++) - { - foreach(QAbstractButton *btn1, bgProto[i]->buttons()) - { - int id1 = bgProto[i]->id(btn1); + // Setup valid subsequent protocols for L2 and L3 protocols + for (int i = ProtoL2; i <= ProtoL3; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); - if (id1 != ButtonIdNone && id1 != ButtonIdOther) - { - int validProtocolCount = 0; + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; - foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) - { - int id2 = bgProto[i+1]->id(btn2); + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); - if (id2 != ButtonIdNone && id2 != ButtonIdOther) - { - if (OstProtocolManager.isValidNeighbour(id1, id2)) - { - connect(btn1, SIGNAL(toggled(bool)), - btn2, SLOT(setEnabled(bool))); - validProtocolCount++; - } - else - connect(btn1, SIGNAL(toggled(bool)), - btn2, SLOT(setDisabled(bool))); - } - } + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager.isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } - // If btn1 has no subsequent valid protocols, - // force subsequent Protocol to 'None' - if (validProtocolCount == 0) - connect(btn1, SIGNAL(clicked(bool)), - bgProto[i+1]->button(ButtonIdNone), SLOT(click())); - } - } - } + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + } + } + } - mpAvailableProtocolsModel = new QStringListModel( - OstProtocolManager.protocolDatabase(), this); - lvAllProtocols->setModel(mpAvailableProtocolsModel); - mpSelectedProtocolsModel = new QStringListModel(this); - lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager.protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); - connect(lvAllProtocols->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(when_lvAllProtocols_selectionChanged( - const QItemSelection&, const QItemSelection&))); - connect(lvSelectedProtocols->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, - const QModelIndex&))); + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); - LoadCurrentStream(); - mpPacketModel = new PacketModel(this); - tvPacketTree->setModel(mpPacketModel); - mpPacketModelTester = new ModelTest(mpPacketModel); - tvPacketTree->header()->hide(); - vwPacketDump->setModel(mpPacketModel); - vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); + mpPacketModelTester = new ModelTest(mpPacketModel); + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); - // TODO(MED): + // TODO(MED): - //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP - rbL3Arp->setHidden(true); - rbL3Ipv6->setHidden(true); - rbL4Icmp->setHidden(true); - rbL4Igmp->setHidden(true); - //! \todo Enable navigation of streams - pbPrev->setDisabled(true); - pbNext->setDisabled(true); - //! \todo Support Goto Stream Id - leStreamId->setDisabled(true); - disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); - //! \todo Support Continuous Mode - rbModeContinuous->setDisabled(true); + //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP + rbL3Arp->setHidden(true); + rbL3Ipv6->setHidden(true); + rbL4Icmp->setHidden(true); + rbL4Igmp->setHidden(true); + //! \todo Enable navigation of streams + pbPrev->setDisabled(true); + pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); - // Finally, restore the saved last selected tab for the various tab widgets - twTopLevel->setCurrentIndex(lastTopLevelTabIndex); + // Finally, restore the saved last selected tab for the various tab widgets + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); } void StreamConfigDialog::setupUiExtra() { - QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); - QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); - QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); - // ---- Setup default stuff that cannot be done in designer ---- - bgProto[ProtoL1] = new QButtonGroup(); - bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); - bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); - bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); - bgProto[ProtoL2] = new QButtonGroup(); + bgProto[ProtoL2] = new QButtonGroup(); #if 0 - foreach(QRadioButton *btn, gbFrameType->findChildren()) - bgL2Proto->addButton(btn); + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); #else - bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); - bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); - bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); - bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); - bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); - bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); #endif - bgProto[ProtoVlan] = new QButtonGroup(); - bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); - bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); - bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); - bgProto[ProtoL3] = new QButtonGroup(); + bgProto[ProtoL3] = new QButtonGroup(); #if 0 - foreach(QRadioButton *btn, gbL3Proto->findChildren()) - bgProto[ProtoL3]->addButton(btn); + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); #else - bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); - bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); - bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); - bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); - bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif - bgProto[ProtoL4] = new QButtonGroup(); + bgProto[ProtoL4] = new QButtonGroup(); #if 0 - foreach(QRadioButton *btn, gbL4Proto->findChildren()) - bgProto[ProtoL4]->addButton(btn); + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); #else - bgProto[ProtoL4]->addButton(rbL4None, 0); - bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); - bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); - bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); - bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); - bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); + bgProto[ProtoL4]->addButton(rbL4None, 0); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); #endif - bgProto[ProtoPayload] = new QButtonGroup(); + bgProto[ProtoPayload] = new QButtonGroup(); #if 0 - foreach(QRadioButton *btn, gbPayloadProto->findChildren()) - bgProto[ProtoPayload]->addButton(btn); + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); #else - bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); - bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); - bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); #endif - /* - ** Setup Validators - */ - // Meta Data - //! \todo - doesn't seem to work - range validator needs a spinbox? - //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + /* + ** Setup Validators + */ + // Meta Data + //! \todo - doesn't seem to work - range validator needs a spinbox? + //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); - /* - ** Setup Connections - */ - connect(rbSendPackets, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbSendBursts, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbModeFixed, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbModeContinuous, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); } StreamConfigDialog::~StreamConfigDialog() { - delete mpPacketModelTester; - delete mpPacketModel; + delete mpPacketModelTester; + delete mpPacketModel; - for (int i = ProtoMin; i < ProtoMax; i++) - delete bgProto[i]; + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; - delete _iter; - delete mpStream; + delete _iter; + delete mpStream; } void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { - if (mode == "Fixed") - { - lePktLen->setEnabled(true); - lePktLenMin->setDisabled(true); - lePktLenMax->setDisabled(true); - } - else if (mode == "Increment") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else if (mode == "Decrement") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else if (mode == "Random") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else - { - qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); - } + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } } void StreamConfigDialog::on_pbPrev_clicked() { #if 0 - StoreCurrentStream(currStreamIdx); - currStreamIdx--; - LoadCurrentStream(currStreamIdx); + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); - pbPrev->setDisabled((currStreamIdx == 0)); - pbNext->setDisabled((currStreamIdx == 2)); + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); #endif } void StreamConfigDialog::on_pbNext_clicked() { #if 0 - StoreCurrentStream(currStreamIdx); - currStreamIdx++; - LoadCurrentStream(currStreamIdx); + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); - pbPrev->setDisabled((currStreamIdx == 0)); - pbNext->setDisabled((currStreamIdx == 2)); + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); #endif } void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) { - qDebug("%s, index = %d", __FUNCTION__, index); - switch (index) - { - case 0: - updateSelectProtocolsSimpleWidget(); - break; - case 1: - updateSelectProtocolsAdvancedWidget(); - break; - default: - qFatal("%s: unexpected index = %d", __FUNCTION__, index); - } + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } } void StreamConfigDialog::when_lvAllProtocols_selectionChanged( - const QItemSelection &selected, const QItemSelection &deselected) + const QItemSelection &selected, const QItemSelection &deselected) { - int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); - qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); - tbAdd->setEnabled(size > 0); + tbAdd->setEnabled(size > 0); } void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( - const QModelIndex ¤t, const QModelIndex &previous) + const QModelIndex ¤t, const QModelIndex &previous) { - qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); - tbDelete->setEnabled(current.isValid()); - tbUp->setEnabled(current.isValid() && (current.row() != 0)); - tbDown->setEnabled(current.isValid() && - (current.row() != (current.model()->rowCount() - 1))); + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); } void StreamConfigDialog::on_tbAdd_clicked() { - int n = 0; - QModelIndex idx2; - AbstractProtocol *p; - QModelIndexList selection; + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; - selection = lvAllProtocols->selectionModel()->selectedIndexes(); + selection = lvAllProtocols->selectionModel()->selectedIndexes(); - // Validation - if (selection.size() == 0) - return; + // Validation + if (selection.size() == 0) + return; - idx2 = lvSelectedProtocols->currentIndex(); - if (idx2.isValid()) - n = idx2.row(); + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; - p = _iter->next(); - } + p = _iter->next(); + } - foreach(QModelIndex idx, selection) - _iter->insert(OstProtocolManager.createProtocol( - mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager.createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx2); + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); } void StreamConfigDialog::on_tbDelete_clicked() { - int n; - QModelIndex idx; - AbstractProtocol *p; + int n; + QModelIndex idx; + AbstractProtocol *p; - idx = lvSelectedProtocols->currentIndex(); + idx = lvSelectedProtocols->currentIndex(); - // Validation - if (!idx.isValid()) - return; + // Validation + if (!idx.isValid()) + return; - n = idx.row() + 1; + n = idx.row() + 1; - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; - p = _iter->next(); - } + p = _iter->next(); + } - _iter->remove(); - delete p; + _iter->remove(); + delete p; - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx); + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); } void StreamConfigDialog::on_tbUp_clicked() { - int m, n; - QModelIndex idx; - AbstractProtocol *p; + int m, n; + QModelIndex idx; + AbstractProtocol *p; - idx = lvSelectedProtocols->currentIndex(); + idx = lvSelectedProtocols->currentIndex(); - // Validation - if (!idx.isValid() || idx.row() == 0) - return; + // Validation + if (!idx.isValid() || idx.row() == 0) + return; - m = n = idx.row() + 1; + m = n = idx.row() + 1; - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; - p = _iter->next(); - } + p = _iter->next(); + } - _iter->remove(); - _iter->previous(); - _iter->insert(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); } void StreamConfigDialog::on_tbDown_clicked() { - int m, n; - QModelIndex idx; - AbstractProtocol *p; + int m, n; + QModelIndex idx; + AbstractProtocol *p; - idx = lvSelectedProtocols->currentIndex(); + idx = lvSelectedProtocols->currentIndex(); - // Validation - if (!idx.isValid() || idx.row() == idx.model()->rowCount()) - return; + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; - m = n = idx.row() + 1; + m = n = idx.row() + 1; - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; - p = _iter->next(); - } + p = _iter->next(); + } - _iter->remove(); - _iter->next(); - _iter->insert(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); } void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() { - QStringList selProtoList; + QStringList selProtoList; - qDebug("%s", __FUNCTION__); + qDebug("%s", __FUNCTION__); - _iter->toFront(); - while(_iter->hasNext()) - { - AbstractProtocol* p = _iter->next(); - qDebug("%p -- %d", p, p->protocolNumber()); - selProtoList.append(p->shortName()); - } - mpSelectedProtocolsModel->setStringList(selProtoList); + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); } void StreamConfigDialog::on_twTopLevel_currentChanged(int index) { - switch (index) - { - // Protocol Data - case 1: - { - QWidget *selWidget; + switch (index) + { + // Protocol Data + case 1: + { + QWidget *selWidget; - // Hide the ToolBox before modifying it - else we have a crash !!! - tbProtocolData->hide(); + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); - selWidget = tbProtocolData->currentWidget(); + selWidget = tbProtocolData->currentWidget(); - // Remove all existing protocol widgets - while (tbProtocolData->count() > 0) - { - QWidget* w = tbProtocolData->widget(0); - tbProtocolData->removeItem(0); - w->setParent(0); - } + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } - // Repopulate the widgets - _iter->toFront(); - while (_iter->hasNext()) - { - AbstractProtocol* p = _iter->next(); - tbProtocolData->addItem(p->configWidget(), p->name()); - } + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } - tbProtocolData->setCurrentWidget(selWidget); + tbProtocolData->setCurrentWidget(selWidget); - tbProtocolData->show(); - break; - } + tbProtocolData->show(); + break; + } - // Packet View - case 3: - { - StoreCurrentStream(); - mpPacketModel->setSelectedProtocols(*_iter); - break; - } + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } - default: - break; - } + default: + break; + } } void StreamConfigDialog::update_NumPacketsAndNumBursts() { - if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) - leNumPackets->setEnabled(true); - else - leNumPackets->setEnabled(false); + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); - if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) - leNumBursts->setEnabled(true); - else - leNumBursts->setEnabled(false); + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); } #if 0 void StreamConfigDialog::on_lePattern_editingFinished() { - ulong num = 0; - bool isOk; - QString str; + ulong num = 0; + bool isOk; + QString str; - num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); - qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); - lePattern->setText(uintToHexStr(num, str, 4)); - qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); } #endif @@ -554,23 +554,23 @@ Skip protocols upto and including the layer specified. */ bool StreamConfigDialog::skipProtocols(int layer) { - _iter->toFront(); + _iter->toFront(); - for (int i = ProtoMin; i <= layer; i++) - { - if(_iter->hasNext()) - { - int id; - QAbstractButton *btn; + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; - id = _iter->peekNext()->protocolNumber(); - btn = bgProto[i]->button(id); - if (btn) - _iter->next(); - } - } + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } - return true; + return true; } /*! @@ -578,348 +578,348 @@ Protocol choices (except "None" and "Other") for a protocol button group are dis */ void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) { - qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); - foreach(QAbstractButton *btn, protocolGroup->buttons()) - { - int id = protocolGroup->id(btn); + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); - if ((id != ButtonIdNone) && (id != ButtonIdOther)) - btn->setDisabled(checked); - } + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } } void StreamConfigDialog::forceProtocolNone(bool checked) { - QObject *btn; + QObject *btn; - btn = sender(); - Q_ASSERT(btn != NULL); + btn = sender(); + Q_ASSERT(btn != NULL); - qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, - checked, btn, rbL1None, rbFtNone, rbL3None); + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); - if (btn == rbL1None) - { - if (checked) - { - bgProto[ProtoVlan]->button(ButtonIdNone)->click(); - bgProto[ProtoL2]->button(ButtonIdNone)->click(); - bgProto[ProtoPayload]->button(ButtonIdNone)->click(); - } + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } - disableProtocols(bgProto[ProtoVlan], checked); - disableProtocols(bgProto[ProtoL2], checked); - disableProtocols(bgProto[ProtoPayload], checked); - } - else if (btn == rbFtNone) - { - if (checked) - bgProto[ProtoL3]->button(ButtonIdNone)->click(); - disableProtocols(bgProto[ProtoL3], checked); - } - else if (btn == rbL3None) - { - if (checked) - bgProto[ProtoL4]->button(ButtonIdNone)->click(); - disableProtocols(bgProto[ProtoL4], checked); - } - else - { - Q_ASSERT(1 == 0); // Unreachable code! - } + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } } void StreamConfigDialog::updateProtocol(int newId) { - int level; - QButtonGroup *btnGrp; + int level; + QButtonGroup *btnGrp; - btnGrp = static_cast(sender()); - Q_ASSERT(btnGrp != NULL); + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); - level = btnGrp->property("ProtocolLevel").toInt(); - Q_ASSERT(btnGrp == bgProto[level]); + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); - __updateProtocol(level, newId); + __updateProtocol(level, newId); } void StreamConfigDialog::__updateProtocol(int level, int newId) { - int oldId; - QButtonGroup *btnGrp; + int oldId; + QButtonGroup *btnGrp; - Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); - btnGrp = bgProto[level]; - oldId = btnGrp->property("ProtocolId").toInt(); + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); - qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, - level, oldId, newId, isUpdateInProgress); + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); - if (newId == oldId) - return; + if (newId == oldId) + return; - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; - ret = skipProtocols(level-1); - Q_ASSERT(ret == true); + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); - Q_ASSERT(oldId != newId); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - if (level == ProtoPayload) - { - while (_iter->hasNext()) - { - p = _iter->next(); - _iter->remove(); - delete p; - } - } - break; - } - } + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } - btnGrp->setProperty("ProtocolId", newId); - return; + btnGrp->setProperty("ProtocolId", newId); + return; } void StreamConfigDialog::updateSelectProtocolsSimpleWidget() { - int i; - quint32 id; - QAbstractButton *btn; + int i; + quint32 id; + QAbstractButton *btn; - qDebug("%s", __FUNCTION__); + qDebug("%s", __FUNCTION__); - isUpdateInProgress = true; + isUpdateInProgress = true; - // Reset to default state ... - for (i = ProtoMin; i < ProtoMax; i++) - bgProto[i]->button(ButtonIdNone)->click(); + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); - // ... now iterate and update - _iter->toFront(); + // ... now iterate and update + _iter->toFront(); - for (i = ProtoMin; i < ProtoMax; i++) - { - if (!_iter->hasNext()) - goto _done; + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; - id = _iter->next()->protocolNumber(); - btn = bgProto[i]->button(id); + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); - if (btn) - { - if (btn->isEnabled()) - btn->click(); - else - { - btn->setChecked(true); - __updateProtocol(i, id); - } - } - else - { - switch (i) - { - case ProtoVlan: - _iter->previous(); - break; + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; - case ProtoPayload: - goto _other; + case ProtoPayload: + goto _other; - default: - btn = bgProto[ProtoPayload]->button(id); - if (btn && btn->isEnabled()) - { - btn->click(); - break; - } - else - goto _other; - } - } - } + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } - // If more protocol(s) beyond payload ... - if (_iter->hasNext()) - { - i = ProtoPayload; - goto _other; - } + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } - goto _done; + goto _done; _other: - for (int j = i; j < ProtoMax; j++) - { - // VLAN doesn't have a "Other" button - if (j == ProtoVlan) - continue; + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; - bgProto[j]->button(ButtonIdOther)->setChecked(true); - __updateProtocol(j, ButtonIdOther); - } + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } _done: - isUpdateInProgress = false; + isUpdateInProgress = false; } void StreamConfigDialog::LoadCurrentStream() { - QString str; + QString str; - qDebug("loading mpStream %p", mpStream); + qDebug("loading mpStream %p", mpStream); - // Meta Data - { - cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); - lePktLen->setText(str.setNum(mpStream->frameLen())); - lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); - lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); - } + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } - // Protocols - { - updateSelectProtocolsSimpleWidget(); - updateSelectProtocolsAdvancedWidget(); + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); - mpStream->loadProtocolWidgets(); - } + mpStream->loadProtocolWidgets(); + } - // Stream Control - { - switch (mpStream->sendUnit()) - { - case Stream::e_su_packets: - rbSendPackets->setChecked(true); - break; - case Stream::e_su_bursts: - rbSendBursts->setChecked(true); - break; - default: - qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); - } + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } - switch (mpStream->sendMode()) - { - case Stream::e_sm_fixed: - rbModeFixed->setChecked(true); - break; - case Stream::e_sm_continuous: - rbModeContinuous->setChecked(true); - break; - default: - qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); - } + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } - switch(mpStream->nextWhat()) - { - case Stream::e_nw_stop: - rbActionStop->setChecked(true); - break; - case Stream::e_nw_goto_next: - rbActionGotoNext->setChecked(true); - break; - case Stream::e_nw_goto_id: - rbActionGotoStream->setChecked(true); - break; - default: - qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); - } + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } - leNumPackets->setText(QString().setNum(mpStream->numPackets())); - leNumBursts->setText(QString().setNum(mpStream->numBursts())); - lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); - lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); - leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); - // TODO(MED): Change this when we support goto to specific stream - leStreamId->setText(QString("0")); - } - qDebug("loading stream done"); + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + } + qDebug("loading stream done"); } void StreamConfigDialog::StoreCurrentStream() { - QString str; - bool isOk; - Stream *pStream = mpStream; + QString str; + bool isOk; + Stream *pStream = mpStream; - qDebug("storing pStream %p", pStream); + qDebug("storing pStream %p", pStream); - // Meta Data - pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); - pStream->setFrameLen(lePktLen->text().toULong(&isOk)); - pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); - pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); - // Protocols - { - pStream->storeProtocolWidgets(); - } + // Protocols + { + pStream->storeProtocolWidgets(); + } - // Stream Control - { - if (rbSendPackets->isChecked()) - pStream->setSendUnit(Stream::e_su_packets); - if (rbSendBursts->isChecked()) - pStream->setSendUnit(Stream::e_su_bursts); + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); - if (rbModeFixed->isChecked()) - pStream->setSendMode(Stream::e_sm_fixed); - if (rbModeContinuous->isChecked()) - pStream->setSendMode(Stream::e_sm_continuous); + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); - if (rbActionStop->isChecked()) - pStream->setNextWhat(Stream::e_nw_stop); - if (rbActionGotoNext->isChecked()) - pStream->setNextWhat(Stream::e_nw_goto_next); - if (rbActionGotoStream->isChecked()) - pStream->setNextWhat(Stream::e_nw_goto_id); + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); - pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); - pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); - pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); - pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); - pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); - } + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } } void StreamConfigDialog::on_pbOk_clicked() { - OstProto::Stream s; + OstProto::Stream s; - // Store dialog contents into stream - StoreCurrentStream(); + // Store dialog contents into stream + StoreCurrentStream(); - // Copy the data from the "local working copy of stream" to "actual stream" - mpStream->protoDataCopyInto(s); - mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); - qDebug("stream stored"); + qDebug("stream stored"); - lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 5909e8e..fcc3f40 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -8,9 +8,9 @@ #include "packetmodel.h" #include "modeltest.h" -#define MAX_MAC_ITER_COUNT 256 -#define MIN_PKT_LEN 64 -#define MAX_PKT_LEN 1522 +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 1522 /* ** TODO @@ -21,92 +21,92 @@ class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog { - Q_OBJECT + Q_OBJECT public: - StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); - ~StreamConfigDialog(); + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); private: - enum ButtonId - { - ButtonIdNone = 0, - ButtonIdOther = -2 - }; + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; - enum ProtoButtonGroup - { - ProtoMin, - ProtoL1 = 0, - ProtoVlan = 1, - ProtoL2 = 2, - ProtoL3 = 3, - ProtoL4 = 4, - ProtoPayload = 5, - ProtoMax - }; + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoPayload = 5, + ProtoMax + }; - QButtonGroup *bgProto[ProtoMax]; + QButtonGroup *bgProto[ProtoMax]; - QStringListModel *mpAvailableProtocolsModel; - QStringListModel *mpSelectedProtocolsModel; + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; - Port& mPort; - uint mCurrentStreamIndex; + Port& mPort; + uint mCurrentStreamIndex; - Stream *mpStream; - ProtocolListIterator *_iter; + Stream *mpStream; + ProtocolListIterator *_iter; - bool isUpdateInProgress; + bool isUpdateInProgress; - PacketModel *mpPacketModel; - ModelTest *mpPacketModelTester; + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; - // The following static variables are used to track the "selected" tab + // The following static variables are used to track the "selected" tab // for the various tab widgets so that it can be restored when the dialog - // is opened the next time - static int lastTopLevelTabIndex; + // is opened the next time + static int lastTopLevelTabIndex; - void setupUiExtra(); - void updateSelectedProtocols(); - void LoadCurrentStream(); - void StoreCurrentStream(); + void setupUiExtra(); + void updateSelectedProtocols(); + void LoadCurrentStream(); + void StoreCurrentStream(); private slots: - void on_cmbPktLenMode_currentIndexChanged(QString mode); - void update_NumPacketsAndNumBursts(); + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); - void on_twTopLevel_currentChanged(int index); - void on_tbSelectProtocols_currentChanged(int index); + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); - // "Simple" Protocol Selection related - bool skipProtocols(int layer); + // "Simple" Protocol Selection related + bool skipProtocols(int layer); - void disableProtocols(QButtonGroup *protocolGroup, bool checked); - void forceProtocolNone(bool checked); + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); - void updateProtocol(int newId); - void __updateProtocol(int level, int newId); + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); - void updateSelectProtocolsSimpleWidget(); + void updateSelectProtocolsSimpleWidget(); - // "Advanced" Protocol Selection related - void when_lvAllProtocols_selectionChanged( - const QItemSelection &selected, const QItemSelection &deselected); - void when_lvSelectedProtocols_currentChanged( - const QModelIndex ¤t, const QModelIndex &previous); + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); - void on_tbAdd_clicked(); - void on_tbDelete_clicked(); - void on_tbUp_clicked(); - void on_tbDown_clicked(); + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); - void updateSelectProtocolsAdvancedWidget(); + void updateSelectProtocolsAdvancedWidget(); - void on_pbPrev_clicked(); - void on_pbNext_clicked(); + void on_pbPrev_clicked(); + void on_pbNext_clicked(); - void on_pbOk_clicked(); + void on_pbOk_clicked(); }; #endif diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp index f67b99e..f701c13 100644 --- a/client/streamlistdelegate.cpp +++ b/client/streamlistdelegate.cpp @@ -13,163 +13,163 @@ StreamListDelegate::StreamListDelegate(QObject *parent) QWidget *StreamListDelegate::createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const + const QStyleOptionViewItem &option, + const QModelIndex &index) const { - QWidget *editor = NULL; + QWidget *editor = NULL; - switch ((StreamModel::StreamFields) index.column()) - { - case StreamModel::StreamStatus: - { - editor = new QCheckBox(parent); - goto _handled; - } - case StreamModel::StreamNextWhat: - { - editor = new QComboBox(parent); - static_cast(editor)->insertItems(0, - StreamModel::nextWhatOptionList()); - goto _handled; - } + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } - case StreamModel::StreamIcon: - case StreamModel::StreamName: - default: - break; - } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } - editor = QItemDelegate::createEditor(parent, option, index); + editor = QItemDelegate::createEditor(parent, option, index); _handled: - return editor; + return editor; } void StreamListDelegate::setEditorData(QWidget *editor, - const QModelIndex &index) const + const QModelIndex &index) const { - switch ((StreamModel::StreamFields) index.column()) - { - case StreamModel::StreamStatus: - { - QCheckBox *cb = static_cast(editor); - cb->setChecked( - index.model()->data(index, Qt::EditRole).toBool()); - goto _handled; - } - case StreamModel::StreamNextWhat: - { - QComboBox *cb = static_cast(editor); - cb->setCurrentIndex( - index.model()->data(index, Qt::EditRole).toInt()); - goto _handled; - } + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } - case StreamModel::StreamIcon: - case StreamModel::StreamName: - default: - break; - } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } - QItemDelegate::setEditorData(editor, index); + QItemDelegate::setEditorData(editor, index); _handled: - return; + return; } void StreamListDelegate::setModelData(QWidget *editor, - QAbstractItemModel *model, const QModelIndex &index) const + QAbstractItemModel *model, const QModelIndex &index) const { - switch ((StreamModel::StreamFields) index.column()) - { - case StreamModel::StreamStatus: - { - QCheckBox *cb = static_cast(editor); - model->setData(index, cb->isChecked(), Qt::EditRole); - goto _handled; - } + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } - case StreamModel::StreamNextWhat: - { - QComboBox *cb = static_cast(editor); - model->setData(index, cb->currentIndex(), Qt::EditRole); - goto _handled; - } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } - case StreamModel::StreamIcon: - case StreamModel::StreamName: - default: - break; - } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } - QItemDelegate::setModelData(editor, model, index); + QItemDelegate::setModelData(editor, model, index); _handled: - return; + return; } void StreamListDelegate::updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, const QModelIndex &index) const + const QStyleOptionViewItem &option, const QModelIndex &index) const { - switch ((StreamModel::StreamFields) index.column()) - { - case StreamModel::StreamStatus: - { - /* - * extra 'coz QItemDelegate does it - otherwise the editor - * placement is incorrect - */ - int extra = 2 * (qApp->style()->pixelMetric( - QStyle::PM_FocusFrameHMargin, 0) + 1); + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); - editor->setGeometry(option.rect.translated(extra, 0)); - goto _handled; - } - case StreamModel::StreamIcon: - case StreamModel::StreamName: - case StreamModel::StreamNextWhat: - default: - break; - } + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } - QItemDelegate::updateEditorGeometry(editor, option, index); + QItemDelegate::updateEditorGeometry(editor, option, index); _handled: - return; + return; } bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) + const QStyleOptionViewItem &option, const QModelIndex &index) { - /* + /* * Special Handling so that user can use the "Stream status" checkbox - * without double clicking first. Copied from QItemDelegate::editorEvent() - * and modified suitably - */ - if ((StreamModel::StreamFields)index.column() == - StreamModel::StreamStatus) - { - // make sure that we have the right event type - if ((event->type() == QEvent::MouseButtonRelease) - || (event->type() == QEvent::MouseButtonDblClick)) - { - QRect checkRect = check(option, option.rect, Qt::Checked); - QRect emptyRect; - doLayout(option, &checkRect, &emptyRect, &emptyRect, false); - if (!checkRect.contains(static_cast(event)->pos())) - return false; + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; - Qt::CheckState state = (static_cast(index.data( - Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); - return model->setData(index, state, Qt::CheckStateRole); - } - } + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } - return QItemDelegate::editorEvent(event, model, option, index); + return QItemDelegate::editorEvent(event, model, option, index); } diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h index a4e8d35..6a52aaa 100644 --- a/client/streamlistdelegate.h +++ b/client/streamlistdelegate.h @@ -9,20 +9,20 @@ class StreamListDelegate : public QItemDelegate Q_OBJECT public: - StreamListDelegate(QObject *parent = 0); + StreamListDelegate(QObject *parent = 0); - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; - void setEditorData(QWidget *editor, const QModelIndex &index) const; - void setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; - void updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, const QModelIndex &index) const; + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; - bool editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index); + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); }; #endif diff --git a/client/streammodel.cpp b/client/streammodel.cpp index a3036fb..925be4b 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -4,239 +4,239 @@ #include "qicon.h" StreamModel::StreamModel(PortGroupList *p, QObject *parent) - : QAbstractTableModel(parent) + : QAbstractTableModel(parent) { - pgl = p; - mCurrentPort = NULL; + pgl = p; + mCurrentPort = NULL; } int StreamModel::rowCount(const QModelIndex &parent) const { - if (parent.isValid()) - return 0; + if (parent.isValid()) + return 0; - if (mCurrentPort) - return mCurrentPort->numStreams(); - else - return 0; + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; } int StreamModel::columnCount(const QModelIndex &parent ) const { - return (int) StreamMaxFields; + return (int) StreamMaxFields; } Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const { - Qt::ItemFlags flags = QAbstractTableModel::flags(index); + Qt::ItemFlags flags = QAbstractTableModel::flags(index); - switch (index.column()) - { - case StreamIcon: - break; - case StreamName: - flags |= Qt::ItemIsEditable; - break; - case StreamStatus: - flags |= Qt::ItemIsUserCheckable; - break; - case StreamNextWhat: - flags |= Qt::ItemIsEditable; - break; - default: - //qFatal("Missed case in switch!"); - break; - } + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } - return flags; + return flags; } QVariant StreamModel::data(const QModelIndex &index, int role) const { - // Check for a valid index - if (!index.isValid()) - return QVariant(); + // Check for a valid index + if (!index.isValid()) + return QVariant(); - // Check for row/column limits - if (index.row() >= mCurrentPort->numStreams()) - return QVariant(); + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); - if (index.column() >= StreamMaxFields) - return QVariant(); + if (index.column() >= StreamMaxFields) + return QVariant(); - if (mCurrentPort == NULL) - return QVariant(); + if (mCurrentPort == NULL) + return QVariant(); - // Return data based on field and role - switch(index.column()) - { - case StreamIcon: - { - if (role == Qt::DecorationRole) - return QIcon(":/icons/stream_edit.png"); - else - return QVariant(); - break; - } - case StreamName: - { - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->streamByIndex(index.row())->name(); - else - return QVariant(); - break; - } - case StreamStatus: - { - if ((role == Qt::CheckStateRole)) - { - if (mCurrentPort->streamByIndex(index.row())->isEnabled()) - return Qt::Checked; - else - return Qt::Unchecked; - } - else - return QVariant(); - break; - } - case StreamNextWhat: - { - int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); - if (role == Qt::DisplayRole) - return nextWhatOptionList().at(val); - else if (role == Qt::EditRole) - return val; - else - return QVariant(); + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); - break; - } - default: - qFatal("-------------UNHANDLED STREAM FIELD----------------"); - } + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } - return QVariant(); + return QVariant(); } bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (mCurrentPort == NULL) - return false; + if (mCurrentPort == NULL) + return false; - if (index.isValid()) - { - switch (index.column()) - { - // Edit Supported Fields - case StreamName: - mCurrentPort->streamByIndex(index.row())->setName(value.toString()); - emit(dataChanged(index, index)); - return true; + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; - case StreamStatus: - mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); - emit(dataChanged(index, index)); - return true; + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; - case StreamNextWhat: - if (role == Qt::EditRole) - { - mCurrentPort->streamByIndex(index.row())->setNextWhat( - (Stream::NextWhat)value.toInt()); - emit(dataChanged(index, index)); - return true; - } - else - return false; + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; - // Edit Not Supported Fields - case StreamIcon: - return false; + // Edit Not Supported Fields + case StreamIcon: + return false; - // Unhandled Stream Field - default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); - break; - } - } + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } - return false; + return false; } QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) - return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); - if (orientation == Qt::Horizontal) - { - switch(section) - { - case StreamIcon: - return QString(""); - break; - case StreamName: - return QString("Name"); - break; - case StreamStatus: - return QString(""); - break; - case StreamNextWhat: - return QString("Goto"); - break; - default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); - break; - } - } - else - return QString("%1").arg(section+1); + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); - return QVariant(); + return QVariant(); } bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) { - qDebug("insertRows() row = %d", row); - qDebug("insertRows() count = %d", count); - beginInsertRows(QModelIndex(), row, row+count-1); - for (int i = 0; i < count; i++) - mCurrentPort->newStreamAt(row); - endInsertRows(); + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); - return true; + return true; } bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) { - qDebug("removeRows() row = %d", row); - qDebug("removeRows() count = %d", count); - beginRemoveRows(QModelIndex(), row, row+count-1); - for (int i = 0; i < count; i++) - { - mCurrentPort->deleteStreamAt(row); - } - endRemoveRows(); + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); - return true; + return true; } // --------------------- SLOTS ------------------------ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) { - if (!current.isValid() || !pgl->isPort(current)) - { - qDebug("current is either invalid or not a port"); - mCurrentPort = NULL; - } - else - { - qDebug("change to valid port"); - quint16 pg = current.internalId() >> 16; - mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; - } - reset(); + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + } + reset(); } diff --git a/client/streammodel.h b/client/streammodel.h index 95af6fe..c65b4d9 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -9,48 +9,48 @@ class PortGroupList; class StreamModel : public QAbstractTableModel { - Q_OBJECT + Q_OBJECT - Port *mCurrentPort; - PortGroupList *pgl; + Port *mCurrentPort; + PortGroupList *pgl; - public: - StreamModel(PortGroupList *p, QObject *parent = 0); + public: + StreamModel(PortGroupList *p, QObject *parent = 0); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole); - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - bool insertRows (int row, int count, - const QModelIndex & parent = QModelIndex()); - bool removeRows (int row, int count, - const QModelIndex & parent = QModelIndex()); - + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + #if 0 // CleanedUp! - // FIXME(HIGH): This *is* like a kludge - QList* currentPortStreamList() - { return &mCurrentPort->mStreams; } + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } #endif - public: - enum StreamFields { - StreamIcon = 0, - StreamName, - StreamStatus, - StreamNextWhat, + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, - StreamMaxFields - }; + StreamMaxFields + }; - static QStringList nextWhatOptionList() - { return QStringList() << "Stop" << "Next" << "Goto first"; } + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } - public slots: - void setCurrentPortIndex(const QModelIndex ¤t); + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); }; diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 025a745..8773154 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -24,12 +24,12 @@ */ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) { - //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); - mpStream = stream; - this->parent = parent; - prev = next = NULL; - metaCount = -1; - protoSize = -1; + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + metaCount = -1; + protoSize = -1; } AbstractProtocol::~AbstractProtocol() @@ -37,15 +37,15 @@ AbstractProtocol::~AbstractProtocol() } AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return NULL; + return NULL; } quint32 AbstractProtocol::protocolNumber() const { - qFatal("Something wrong!!!"); - return 0xFFFFFFFF; + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; } /*! @@ -64,7 +64,7 @@ quint32 AbstractProtocol::protocolNumber() const The default implementation returns a null string */ QString AbstractProtocol::name() const { - return QString(); + return QString(); } /*! Returns the short name or abbreviation of the protocol \n @@ -74,27 +74,27 @@ QString AbstractProtocol::name() const and subsequently returns the cached abbreviation */ QString AbstractProtocol::shortName() const { - if (protoAbbr.isNull()) - { - QString abbr; + if (protoAbbr.isNull()) + { + QString abbr; - for (int i = 0; i < name().size(); i++) - if (name().at(i).isUpper()) abbr.append(name().at(i)); + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); - if (abbr.size()) - protoAbbr = abbr; - else - protoAbbr = QString(""); - } + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } - return protoAbbr; + return protoAbbr; } /*! Returns the number of fields (both Frame and Meta fields) \n The default implementation returns zero */ -int AbstractProtocol::fieldCount() const +int AbstractProtocol::fieldCount() const { - return 0; + return 0; } /*! Returns the number of meta fields \n @@ -102,31 +102,31 @@ int AbstractProtocol::fieldCount() const the FieldIsMeta flag is set\n The default implementation caches the count on its first invocation and subsequently returns the cached count */ -int AbstractProtocol::metaFieldCount() const +int AbstractProtocol::metaFieldCount() const { - if (metaCount < 0) - { - int c = 0; - for (int i = 0; i < fieldCount() ; i++) - if (fieldFlags(i).testFlag(FieldIsMeta)) - c++; - metaCount = c; - } + if (metaCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FieldIsMeta)) + c++; + metaCount = c; + } - return metaCount; + return metaCount; } /*! Returns the number of frame fields \n Convenience method - same as fieldCount() minus metaFieldCount() */ -int AbstractProtocol::frameFieldCount() const +int AbstractProtocol::frameFieldCount() const { - //qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount()); - return (fieldCount() - metaFieldCount()); + //qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount()); + return (fieldCount() - metaFieldCount()); } AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const { - return FieldIsNormal; + return FieldIsNormal; } /*! Returns the requested field attribute data \n @@ -157,116 +157,116 @@ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const - protocolFramePayloadSize() */ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (attrib) - { - case FieldName: - return QString(); - case FieldBitSize: - Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum), - "AbstractProtocol::fieldData()", - "FieldBitSize for checksum fields need to be handled by the subclass"); - return fieldData(index, FieldFrameValue, streamIndex). - toByteArray().size() * 8; - case FieldValue: - return 0; - case FieldFrameValue: - return QByteArray(); - case FieldTextValue: - return QString(); + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); - default: - qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, - attrib); - } + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } - return QVariant(); + return QVariant(); } /*! Sets the value of a field corresponding to index \n Returns true if field is successfully set, false otherwise \n The default implementation always returns false */ bool AbstractProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const { - return ProtocolIdNone; + return ProtocolIdNone; } quint32 AbstractProtocol::protocolId(ProtocolIdType type) const { - return 0; + return 0; } quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const { - quint32 id; + quint32 id; - if (next) - id = next->protocolId(type); - else if (parent) - id = parent->payloadProtocolId(type); - else - id = 0xFFFFFFFF; + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; - qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); - return id; + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; } int AbstractProtocol::protocolFrameSize(int streamIndex) const { - if (protoSize < 0) - { - int bitsize = 0; + if (protoSize < 0) + { + int bitsize = 0; - for (int i = 0; i < fieldCount(); i++) - { - if (!fieldFlags(i).testFlag(FieldIsMeta)) - bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); - } - protoSize = (bitsize+7)/8; - } + for (int i = 0; i < fieldCount(); i++) + { + if (!fieldFlags(i).testFlag(FieldIsMeta)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } - qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); - return protoSize; + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; } int AbstractProtocol::protocolFrameOffset(int streamIndex) const { - int size = 0; - AbstractProtocol *p = prev; - while (p) - { - size += p->protocolFrameSize(streamIndex); - p = p->prev; - } + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } - if (parent) - size += parent->protocolFrameOffset(streamIndex); + if (parent) + size += parent->protocolFrameOffset(streamIndex); - qDebug("%s: ofs = %d", __FUNCTION__, size); - return size; + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; } int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const { - int size = 0; - AbstractProtocol *p = next; - while (p) - { - size += p->protocolFrameSize(streamIndex); - p = p->next; - } - if (parent) - size += parent->protocolFramePayloadSize(streamIndex); + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); - qDebug("%s: payloadSize = %d", __FUNCTION__, size); - return size; + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; } @@ -277,136 +277,136 @@ int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const which are not an integral number of bytes\n */ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const { - QByteArray proto, field; - uint bits, lastbitpos = 0; - FieldFlags flags; + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; - for (int i=0; i < fieldCount() ; i++) - { - flags = fieldFlags(i); - if (!flags.testFlag(FieldIsMeta)) - { - bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); - if (bits == 0) - continue; - Q_ASSERT(bits > 0); + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (!flags.testFlag(FieldIsMeta)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); - if (forCksum && flags.testFlag(FieldIsCksum)) - { - field.resize((bits+7)/8); - field.fill('\0'); - } - else - field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); - qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size()); + if (forCksum && flags.testFlag(FieldIsCksum)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size()); - if (bits == (uint) field.size() * 8) - { - if (lastbitpos == 0) - proto.append(field); - else - { - Q_ASSERT(field.size() > 0); + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); - char c = proto[proto.size() - 1]; - proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); - for (int j = 0; j < field.size() - 1; j++) - proto.append(field.at(j) << lastbitpos | - field.at(j+1) >> lastbitpos); - } - } - else if (bits < (uint) field.size() * 8) - { - uchar c; - uint v; + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + field.at(j+1) >> lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; - v = (field.size()*8) - bits; + v = (field.size()*8) - bits; - Q_ASSERT(v < 8); + Q_ASSERT(v < 8); - if (lastbitpos == 0) - { - for (int j = 0; j < field.size(); j++) - { - c = field.at(j) << v; - if ((j+1) < field.size()) - c |= ((uchar)field.at(j+1) >> (8-v)); - proto.append(c); - } + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } - lastbitpos = (lastbitpos + bits) % 8; - } - else - { - Q_ASSERT(proto.size() > 0); + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); - for (int j = 0; j < field.size(); j++) - { - uchar d; + for (int j = 0; j < field.size(); j++) + { + uchar d; - c = field.at(j) << v; - if ((j+1) < field.size()) - c |= ((uchar) field.at(j+1) >> (8-v)); - d = proto[proto.size() - 1]; - proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); - if (bits > (8*j + (8 - v))) - proto.append(c << (8-lastbitpos)); - } + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } - lastbitpos = (lastbitpos + bits) % 8; - } - } - else // if (bits > field.size() * 8) - { - qFatal("bitsize more than FrameValue size. skipping..."); - continue; - } - } - } + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } - return proto; + return proto; } bool AbstractProtocol::isProtocolFrameValueVariable() const { - return false; + return false; } bool AbstractProtocol::isProtocolFrameSizeVariable() const { - return false; + return false; } bool AbstractProtocol::isProtocolFramePayloadValueVariable() const { - AbstractProtocol *p = next; + AbstractProtocol *p = next; - while (p) - { - if (p->isProtocolFrameValueVariable()) - return true; - p = p->next; - } - if (parent && parent->isProtocolFramePayloadValueVariable()) - return true; + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; - return false; + return false; } bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const { - AbstractProtocol *p = next; + AbstractProtocol *p = next; - while (p) - { - if (p->isProtocolFrameSizeVariable()) - return true; - p = p->next; - } - if (parent && parent->isProtocolFramePayloadSizeVariable()) - return true; + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; - return false; + return false; } @@ -418,124 +418,124 @@ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const to prevent infinite recursion */ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType) const { - static int recursionCount = 0; - quint32 cksum = 0xFFFFFFFF; + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; - recursionCount++; - Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); - switch(cksumType) - { - case CksumIp: - { - QByteArray fv; - quint16 *ip; - quint32 len, sum = 0; + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; - fv = protocolFrameValue(streamIndex, true); - ip = (quint16*) fv.constData(); - len = fv.size(); + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); - while(len > 1) - { - sum += *ip; - if(sum & 0x80000000) - sum = (sum & 0xFFFF) + (sum >> 16); - ip++; - len -= 2; - } + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } - if (len) - sum += (unsigned short) *(unsigned char *)ip; + if (len) + sum += (unsigned short) *(unsigned char *)ip; - while(sum>>16) - sum = (sum & 0xFFFF) + (sum >> 16); + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); - cksum = qFromBigEndian((quint16) ~sum); - break; - } + cksum = qFromBigEndian((quint16) ~sum); + break; + } - case CksumTcpUdp: - { - quint16 cks; - quint32 sum = 0; + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; - cks = protocolFrameCksum(streamIndex, CksumIp); - sum += (quint16) ~cks; - cks = protocolFramePayloadCksum(streamIndex, CksumIp); - sum += (quint16) ~cks; - cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); - sum += (quint16) ~cks; + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; - while(sum>>16) - sum = (sum & 0xFFFF) + (sum >> 16); + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); - cksum = (~sum) & 0xFFFF; - break; - } - default: - break; - } + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } - recursionCount--; - return cksum; + recursionCount--; + return cksum; } quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType) const { - quint32 sum = 0; - quint16 cksum; - AbstractProtocol *p = prev; + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; - Q_ASSERT(cksumType == CksumIpPseudo); + Q_ASSERT(cksumType == CksumIpPseudo); - while (p) - { - cksum = p->protocolFrameCksum(streamIndex, cksumType); - sum += (quint16) ~cksum; - p = p->prev; - qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); - } - if (parent) - { - cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType); - sum += (quint16) ~cksum; - } + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + p = p->prev; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + } - while(sum>>16) - sum = (sum & 0xFFFF) + (sum >> 16); + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); - return (quint16) ~sum; + return (quint16) ~sum; } quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType) const { - quint32 sum = 0; - quint16 cksum; - AbstractProtocol *p = next; + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; - Q_ASSERT(cksumType == CksumIp); + Q_ASSERT(cksumType == CksumIp); - while (p) - { - cksum = p->protocolFrameCksum(streamIndex, cksumType); - sum += (quint16) ~cksum; - p = p->next; - } + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + p = p->next; + } - if (parent) - { - cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType); - sum += (quint16) ~cksum; - } + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + } - while(sum>>16) - sum = (sum & 0xFFFF) + (sum >> 16); + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); - return (quint16) ~sum; + return (quint16) ~sum; } diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index c520a31..fb6a703 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -16,108 +16,108 @@ #define BASE_DEC (10) #define BASE_HEX (16) -#define uintToHexStr(num, bytes) \ - QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) class StreamBase; class ProtocolListIterator; class AbstractProtocol { - template - friend class ComboProtocol; - friend class ProtocolListIterator; + template + friend class ComboProtocol; + friend class ProtocolListIterator; private: - mutable int metaCount; - mutable int protoSize; - mutable QString protoAbbr; + mutable int metaCount; + mutable int protoSize; + mutable QString protoAbbr; protected: - StreamBase *mpStream; - AbstractProtocol *parent; - AbstractProtocol *prev; - AbstractProtocol *next; + StreamBase *mpStream; + AbstractProtocol *parent; + AbstractProtocol *prev; + AbstractProtocol *next; public: - enum FieldFlag { - FieldIsNormal = 0x0, - FieldIsMeta = 0x1, - FieldIsCksum = 0x2 - }; - Q_DECLARE_FLAGS(FieldFlags, FieldFlag); + enum FieldFlag { + FieldIsNormal = 0x0, + FieldIsMeta = 0x1, + FieldIsCksum = 0x2 + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); - enum FieldAttrib { - FieldName, //! name - FieldValue, //! value in host byte order (user editable) - FieldTextValue, //! value as text - FieldFrameValue, //! frame encoded value in network byte order - FieldBitSize, //! size in bits - }; + enum FieldAttrib { + FieldName, //! name + FieldValue, //! value in host byte order (user editable) + FieldTextValue, //! value as text + FieldFrameValue, //! frame encoded value in network byte order + FieldBitSize, //! size in bits + }; - enum ProtocolIdType { - ProtocolIdNone, - ProtocolIdLlc, - ProtocolIdEth, - ProtocolIdIp, - }; + enum ProtocolIdType { + ProtocolIdNone, + ProtocolIdLlc, + ProtocolIdEth, + ProtocolIdIp, + }; - enum CksumType { - CksumIp, - CksumIpPseudo, - CksumTcpUdp, + enum CksumType { + CksumIp, + CksumIpPseudo, + CksumTcpUdp, - CksumMax - }; + CksumMax + }; - AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~AbstractProtocol(); + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual ProtocolIdType protocolIdType() const; - virtual quint32 protocolId(ProtocolIdType type) const; - quint32 payloadProtocolId(ProtocolIdType type) const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; - virtual int fieldCount() const; - int metaFieldCount() const; - int frameFieldCount() const; + virtual int fieldCount() const; + int metaFieldCount() const; + int frameFieldCount() const; - virtual FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - QByteArray protocolFrameValue(int streamIndex = 0, - bool forCksum = false) const; - virtual int protocolFrameSize(int streamIndex = 0) const; - int protocolFrameOffset(int streamIndex = 0) const; - int protocolFramePayloadSize(int streamIndex = 0) const; + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; - virtual bool isProtocolFrameValueVariable() const; - virtual bool isProtocolFrameSizeVariable() const; - bool isProtocolFramePayloadValueVariable() const; - bool isProtocolFramePayloadSizeVariable() const; + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; - virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; - quint32 protocolFrameHeaderCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; - quint32 protocolFramePayloadCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; - virtual QWidget* configWidget() = 0; - virtual void loadConfigWidget() = 0; - virtual void storeConfigWidget() = 0; + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); diff --git a/common/comboprotocol.h b/common/comboprotocol.h index dbfd682..9d10348 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -7,185 +7,185 @@ template class ComboProtocol : public AbstractProtocol { private: - ProtoA *protoA; - ProtoB *protoB; - QWidget *configForm; + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; public: - ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) - : AbstractProtocol(stream, parent) - { - protoA = new ProtoA(stream, this); - protoB = new ProtoB(stream, this); - protoA->next = protoB; - protoB->prev = protoA; - configForm = NULL; + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; - qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, - protoNumber, protoA, protoB); - } + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } - virtual ~ComboProtocol() - { - if (configForm) - { - protoA->configWidget()->setParent(0); - protoB->configWidget()->setParent(0); - delete configForm; - } - delete protoA; - delete protoB; - } + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } - static ComboProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent) - { - return new ComboProtocol(stream, parent); - } + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } - virtual quint32 protocolNumber() const - { - return protoNumber; - } + virtual quint32 protocolNumber() const + { + return protoNumber; + } - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const - { - protoA->protoDataCopyInto(protocol); - protoB->protoDataCopyInto(protocol); - protocol.mutable_protocol_id()->set_id(protocolNumber()); - } + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) - { - if (protocol.protocol_id().id() == protocolNumber()) - { - OstProto::Protocol proto; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; - // NOTE: To use protoX->protoDataCopyFrom() we need to arrange - // so that it sees its own protocolNumber() - but since the - // input param 'protocol' is 'const', we make a copy first + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we make a copy first - proto.CopyFrom(protocol); + proto.CopyFrom(protocol); - proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); - protoA->protoDataCopyFrom(proto); + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); - proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); - protoB->protoDataCopyFrom(proto); - } - } + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } - virtual QString name() const - { - return protoA->name() + "/" + protoB->name(); - } - virtual QString shortName() const - { - return protoA->shortName() + "/" + protoB->shortName(); - } + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } - virtual ProtocolIdType protocolIdType() const - { - return protoB->protocolIdType(); - } + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } - virtual quint32 protocolId(ProtocolIdType type) const - { - return protoA->protocolId(type); - } - //quint32 payloadProtocolId(ProtocolIdType type) const; + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; - virtual int fieldCount() const - { - return protoA->fieldCount() + protoB->fieldCount(); - } - //virtual int metaFieldCount() const; - //int frameFieldCount() const; + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; - virtual FieldFlags fieldFlags(int index) const - { - int cnt = protoA->fieldCount(); + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); - if (index < cnt) - return protoA->fieldFlags(index); - else - return protoB->fieldFlags(index - cnt); - } - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const - { - int cnt = protoA->fieldCount(); + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); - if (index < cnt) - return protoA->fieldData(index, attrib, streamIndex); - else - return protoB->fieldData(index - cnt, attrib, streamIndex); - } - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue) - { - int cnt = protoA->fieldCount(); + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); - if (index < cnt) - return protoA->setFieldData(index, value, attrib); - else - return protoB->setFieldData(index - cnt, value, attrib); - } + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } #if 0 - QByteArray protocolFrameValue(int streamIndex = 0, - bool forCksum = false) const; - virtual int protocolFrameSize() const; - int protocolFrameOffset() const; - int protocolFramePayloadSize() const; + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; #endif - virtual bool isProtocolFrameValueVariable() const - { - return (protoA->isProtocolFrameValueVariable() - || protoB->isProtocolFrameValueVariable()); - } + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } - virtual bool isProtocolFrameSizeVariable() const - { - return (protoA->isProtocolFrameSizeVariable() - || protoB->isProtocolFrameSizeVariable()); - } + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } #if 0 - virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; - quint32 protocolFrameHeaderCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; - quint32 protocolFramePayloadCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; #endif - virtual QWidget* configWidget() - { - if (configForm == NULL) - { - QVBoxLayout *layout = new QVBoxLayout; + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; - configForm = new QWidget; - layout->addWidget(protoA->configWidget()); - layout->addWidget(protoB->configWidget()); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - configForm->setLayout(layout); - } - return configForm; - } - virtual void loadConfigWidget() - { - protoA->loadConfigWidget(); - protoB->loadConfigWidget(); - } - virtual void storeConfigWidget() - { - protoA->storeConfigWidget(); - protoB->storeConfigWidget(); - } + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } }; #endif diff --git a/common/dot2llc.h b/common/dot2llc.h index 1553549..44f6a3b 100644 --- a/common/dot2llc.h +++ b/common/dot2llc.h @@ -6,6 +6,6 @@ #include "llc.h" typedef ComboProtocol Dot2LlcProtocol; + Dot3Protocol, LlcProtocol> Dot2LlcProtocol; #endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto index 3aa1ca4..51eb756 100644 --- a/common/dot2llc.proto +++ b/common/dot2llc.proto @@ -6,9 +6,9 @@ package OstProto; // 802.2 LLC message Dot2Llc { - // Empty since this is a 'combo' protocol + // Empty since this is a 'combo' protocol } extend Protocol { - optional Dot2Llc dot2Llc = 127; + optional Dot2Llc dot2Llc = 127; } diff --git a/common/dot2snap.h b/common/dot2snap.h index 3eee505..2ad3ead 100644 --- a/common/dot2snap.h +++ b/common/dot2snap.h @@ -6,6 +6,6 @@ #include "snap.h" typedef ComboProtocol Dot2SnapProtocol; + Dot2LlcProtocol, SnapProtocol> Dot2SnapProtocol; #endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto index b9a03f5..ce41b8f 100644 --- a/common/dot2snap.proto +++ b/common/dot2snap.proto @@ -4,9 +4,9 @@ package OstProto; // 802.2 SNAP message Dot2Snap { - // Empty since this is a 'combo' protocol + // Empty since this is a 'combo' protocol } extend Protocol { - optional Dot2Snap dot2Snap = 128; + optional Dot2Snap dot2Snap = 128; } diff --git a/common/dot3.cpp b/common/dot3.cpp index ac2a878..ddf58a8 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -4,150 +4,150 @@ #include "dot3.h" #include "streambase.h" -#define SZ_FCS 4 +#define SZ_FCS 4 Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } Dot3Protocol::~Dot3Protocol() { - delete configForm; + delete configForm; } AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new Dot3Protocol(stream, parent); + return new Dot3Protocol(stream, parent); } quint32 Dot3Protocol::protocolNumber() const { - return OstProto::Protocol::kDot3FieldNumber; + return OstProto::Protocol::kDot3FieldNumber; } void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::dot3)) - data.MergeFrom(protocol.GetExtension(OstProto::dot3)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); } QString Dot3Protocol::name() const { - return QString("802.3"); + return QString("802.3"); } QString Dot3Protocol::shortName() const { - return QString("802.3"); + return QString("802.3"); } -int Dot3Protocol::fieldCount() const +int Dot3Protocol::fieldCount() const { - return dot3_fieldCount; + return dot3_fieldCount; } QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case dot3_length: - switch(attrib) - { - case FieldName: - return QString("Length"); - case FieldValue: - { - quint16 len; + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len; - //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(streamIndex); - return len; - } - case FieldTextValue: - { - quint16 len; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len; - //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(streamIndex); - return QString("%1").arg(len); - } - case FieldFrameValue: - { - quint16 len; - QByteArray fv; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(streamIndex); + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len; + QByteArray fv; - //len = mpStream->frameLen() - SZ_FCS; - len = protocolFramePayloadSize(streamIndex); - fv.resize(2); - qToBigEndian(len, (uchar*) fv.data()); - return fv; - } - case FieldBitSize: - return 16; - default: - break; - } - break; + //len = mpStream->frameLen() - SZ_FCS; + len = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; - default: - break; - } + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Dot3Protocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } bool Dot3Protocol::isProtocolFrameValueVariable() const { - return isProtocolFramePayloadSizeVariable(); + return isProtocolFramePayloadSizeVariable(); } QWidget* Dot3Protocol::configWidget() { - if (configForm == NULL) - { - configForm = new Dot3ConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; } void Dot3Protocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leLength->setText( - fieldData(dot3_length, FieldValue).toString()); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); } void Dot3Protocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_length(configForm->leLength->text().toULong(&isOk)); + data.set_length(configForm->leLength->text().toULong(&isOk)); } diff --git a/common/dot3.h b/common/dot3.h index 9e0e8c1..af045db 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -8,49 +8,49 @@ class Dot3ConfigForm : public QWidget, public Ui::dot3 { - Q_OBJECT + Q_OBJECT public: - Dot3ConfigForm(QWidget *parent = 0); + Dot3ConfigForm(QWidget *parent = 0); }; class Dot3Protocol : public AbstractProtocol { private: - OstProto::Dot3 data; - Dot3ConfigForm *configForm; - enum Dot3field - { - dot3_length, + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, - dot3_fieldCount - }; + dot3_fieldCount + }; public: - Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~Dot3Protocol(); + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameValueVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/dot3.proto b/common/dot3.proto index 5a84c18..154f554 100644 --- a/common/dot3.proto +++ b/common/dot3.proto @@ -4,9 +4,9 @@ package OstProto; // 802.3 message Dot3 { - optional uint32 length = 1; + optional uint32 length = 1; } extend Protocol { - optional Dot3 dot3 = 122; + optional Dot3 dot3 = 122; } diff --git a/common/eth2.cpp b/common/eth2.cpp index 0028b51..2939fed 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -4,150 +4,150 @@ #include "eth2.h" Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } Eth2Protocol::~Eth2Protocol() { - delete configForm; + delete configForm; } AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new Eth2Protocol(stream, parent); + return new Eth2Protocol(stream, parent); } quint32 Eth2Protocol::protocolNumber() const { - return OstProto::Protocol::kEth2FieldNumber; + return OstProto::Protocol::kEth2FieldNumber; } void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::eth2)) - data.MergeFrom(protocol.GetExtension(OstProto::eth2)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); } QString Eth2Protocol::name() const { - return QString("Ethernet II"); + return QString("Ethernet II"); } QString Eth2Protocol::shortName() const { - return QString("Eth II"); + return QString("Eth II"); } AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const { - return ProtocolIdEth; + return ProtocolIdEth; } -int Eth2Protocol::fieldCount() const +int Eth2Protocol::fieldCount() const { - return eth2_fieldCount; + return eth2_fieldCount; } QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case eth2_type: - { - quint16 type; - switch(attrib) - { - case FieldName: - return QString("Type"); - case FieldValue: - type = payloadProtocolId(ProtocolIdEth); - return type; - case FieldTextValue: - type = payloadProtocolId(ProtocolIdEth); - return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - type = payloadProtocolId(ProtocolIdEth); - fv.resize(2); - qToBigEndian((quint16) type, (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; - } - default: - break; - } + switch (index) + { + case eth2_type: + { + quint16 type; + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + type = payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Eth2Protocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - bool isOk = false; + bool isOk = false; - if (attrib != FieldValue) - return false; + if (attrib != FieldValue) + return false; - switch (index) - { - case eth2_type: - { - uint type = value.toUInt(&isOk); - if (isOk) - data.set_type(type); - } - default: - break; - } - return isOk; + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + } + default: + break; + } + return isOk; } QWidget* Eth2Protocol::configWidget() { - if (configForm == NULL) - { - configForm = new Eth2ConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; } void Eth2Protocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leType->setText(uintToHexStr( - fieldData(eth2_type, FieldValue).toUInt(), 2)); + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); } void Eth2Protocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); } diff --git a/common/eth2.h b/common/eth2.h index fcf6b7d..cae1bb3 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -8,49 +8,49 @@ class Eth2ConfigForm : public QWidget, public Ui::eth2 { - Q_OBJECT + Q_OBJECT public: - Eth2ConfigForm(QWidget *parent = 0); + Eth2ConfigForm(QWidget *parent = 0); }; class Eth2Protocol : public AbstractProtocol { private: - OstProto::Eth2 data; - Eth2ConfigForm *configForm; - enum eth2field - { - eth2_type = 0, + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, - eth2_fieldCount - }; + eth2_fieldCount + }; public: - Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~Eth2Protocol(); + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual ProtocolIdType protocolIdType() const; + virtual ProtocolIdType protocolIdType() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/eth2.proto b/common/eth2.proto index 348888d..c5339a9 100644 --- a/common/eth2.proto +++ b/common/eth2.proto @@ -4,9 +4,9 @@ package OstProto; // Ethernet II message Eth2 { - optional uint32 type = 1; + optional uint32 type = 1; } extend Protocol { - optional Eth2 eth2 = 121; + optional Eth2 eth2 = 121; } diff --git a/common/ip4.cpp b/common/ip4.cpp index 4c28bfb..c2594f3 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -4,704 +4,704 @@ #include "ip4.h" Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); - leIpVersion->setValidator(new QIntValidator(0, 15, this)); + leIpVersion->setValidator(new QIntValidator(0, 15, this)); - connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); - connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); } Ip4ConfigForm::~Ip4ConfigForm() { - qDebug("IPv4 Config Form destructor called"); + qDebug("IPv4 Config Form destructor called"); } void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) { - if (index == OstProto::Ip4::e_im_fixed) - { - leIpSrcAddrCount->setDisabled(true); - leIpSrcAddrMask->setDisabled(true); - } - else - { - leIpSrcAddrCount->setEnabled(true); - leIpSrcAddrMask->setEnabled(true); - } + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } } void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) { - if (index == OstProto::Ip4::e_im_fixed) - { - leIpDstAddrCount->setDisabled(true); - leIpDstAddrMask->setDisabled(true); - } - else - { - leIpDstAddrCount->setEnabled(true); - leIpDstAddrMask->setEnabled(true); - } + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } } Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } Ip4Protocol::~Ip4Protocol() { - delete configForm; + delete configForm; } AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new Ip4Protocol(stream, parent); + return new Ip4Protocol(stream, parent); } quint32 Ip4Protocol::protocolNumber() const { - return OstProto::Protocol::kIp4FieldNumber; + return OstProto::Protocol::kIp4FieldNumber; } void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::ip4)) - data.MergeFrom(protocol.GetExtension(OstProto::ip4)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); } QString Ip4Protocol::name() const { - return QString("Internet Protocol ver 4"); + return QString("Internet Protocol ver 4"); } QString Ip4Protocol::shortName() const { - return QString("IPv4"); + return QString("IPv4"); } AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const { - return ProtocolIdIp; + return ProtocolIdIp; } quint32 Ip4Protocol::protocolId(ProtocolIdType type) const { - switch(type) - { - case ProtocolIdLlc: return 0x060603; - case ProtocolIdEth: return 0x0800; - case ProtocolIdIp: return 0x04; - default:break; - } + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } - return AbstractProtocol::protocolId(type); + return AbstractProtocol::protocolId(type); } -int Ip4Protocol::fieldCount() const +int Ip4Protocol::fieldCount() const { - return ip4_fieldCount; + return ip4_fieldCount; } AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case ip4_ver: - case ip4_hdrLen: - case ip4_tos: - case ip4_totLen: - case ip4_id: - case ip4_flags: - case ip4_fragOfs: - case ip4_ttl: - case ip4_proto: - break; + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; - case ip4_cksum: - flags |= FieldIsCksum; - break; + case ip4_cksum: + flags |= FieldIsCksum; + break; - case ip4_srcAddr: - case ip4_dstAddr: - break; + case ip4_srcAddr: + case ip4_dstAddr: + break; - case ip4_isOverrideVer: - case ip4_isOverrideHdrLen: - case ip4_isOverrideTotLen: - case ip4_isOverrideCksum: - case ip4_srcAddrMode: - case ip4_srcAddrCount: - case ip4_srcAddrMask: - case ip4_dstAddrMode: - case ip4_dstAddrCount: - case ip4_dstAddrMask: - flags |= FieldIsMeta; - break; + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags |= FieldIsMeta; + break; - default: - break; - } + default: + break; + } - return flags; + return flags; } QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case ip4_ver: - { - int ver; + switch (index) + { + case ip4_ver: + { + int ver; - ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; - switch(attrib) - { - case FieldName: - return QString("Version"); - case FieldValue: - return ver; - case FieldTextValue: - return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); - case FieldFrameValue: - return QByteArray(1, (char) ver); - case FieldBitSize: - return 4; - default: - break; - } - break; - } - case ip4_hdrLen: - { - int hdrlen; + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; - hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; - switch(attrib) - { - case FieldName: - return QString("Header Length"); - case FieldValue: - return hdrlen; - case FieldTextValue: - return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); - case FieldFrameValue: - return QByteArray(1, (char) hdrlen); - case FieldBitSize: - return 4; - default: - break; - } - break; - } - case ip4_tos: - switch(attrib) - { - case FieldName: - return QString("TOS/DSCP"); - case FieldValue: - return data.tos(); - case FieldFrameValue: - return QByteArray(1, (char) data.tos()); - case FieldTextValue: - return QString("0x%1"). - arg(data.tos(), 2, BASE_HEX, QChar('0'));; - default: - break; - } - break; - case ip4_totLen: - { - switch(attrib) - { - case FieldName: - return QString("Total Length"); - case FieldValue: - { - int totlen; - totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize(streamIndex) + 20); - return totlen; - } - case FieldFrameValue: - { - QByteArray fv; - int totlen; - totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize(streamIndex) + 20); - fv.resize(2); - qToBigEndian((quint16) totlen, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - { - int totlen; - totlen = data.is_override_totlen() ? data.totlen() : - (protocolFramePayloadSize(streamIndex) + 20); - return QString("%1").arg(totlen); - } - case FieldBitSize: - return 16; - default: - break; - } - break; - } - case ip4_id: - switch(attrib) - { - case FieldName: - return QString("Identification"); - case FieldValue: - return data.id(); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.id(), (uchar*)fv.data()); - return fv; - } - case FieldTextValue: - return QString("0x%1"). - arg(data.id(), 2, BASE_HEX, QChar('0'));; - default: - break; - } - break; - case ip4_flags: - switch(attrib) - { - case FieldName: - return QString("Flags"); - case FieldValue: - return data.flags(); - case FieldFrameValue: - return QByteArray(1, (char) data.flags()); - case FieldTextValue: - { - QString s; - s.append("Unused:"); - s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); - s.append(" Don't Fragment:"); - s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); - s.append(" More Fragments:"); - s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); - return s; - } - case FieldBitSize: - return 3; - default: - break; - } - break; - case ip4_fragOfs: - switch(attrib) - { - case FieldName: - return QString("Fragment Offset"); - case FieldValue: - return data.frag_ofs(); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) (data.frag_ofs()), - (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QString("%1").arg(data.frag_ofs()*8); - case FieldBitSize: - return 13; - default: - break; - } - break; - case ip4_ttl: - switch(attrib) - { - case FieldName: - return QString("Time to Live"); - case FieldValue: - return data.ttl(); - case FieldFrameValue: - return QByteArray(1, (char)data.ttl()); - case FieldTextValue: - return QString("%1").arg(data.ttl()); - default: - break; - } - break; - case ip4_proto: - { - switch(attrib) - { - case FieldName: - return QString("Protocol"); - case FieldValue: - { - unsigned char id = payloadProtocolId(ProtocolIdIp); - return id; - } - case FieldFrameValue: - { - unsigned char id = payloadProtocolId(ProtocolIdIp); - return QByteArray(1, (char) id); - } - case FieldTextValue: - { - unsigned char id = payloadProtocolId(ProtocolIdIp); - return QString("0x%1"). - arg(id, 2, BASE_HEX, QChar('0')); - } - default: - break; - } - break; - } - case ip4_cksum: - { - switch(attrib) - { - case FieldName: - return QString("Header Checksum"); - case FieldValue: - { - quint16 cksum; + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumIp); - return cksum; - } - case FieldFrameValue: - { - QByteArray fv; - quint16 cksum; + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumIp); + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); - fv.resize(2); - qToBigEndian((quint16) cksum, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - { - quint16 cksum; + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumIp); - return QString("0x%1"). - arg(cksum, 4, BASE_HEX, QChar('0'));; - } - case FieldBitSize: - return 16; - default: - break; - } - break; - } - case ip4_srcAddr: - { - int u; - quint32 subnet, host, srcIp = 0; + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; - switch(data.src_ip_mode()) - { - case OstProto::Ip4::e_im_fixed: - srcIp = data.src_ip(); - break; - case OstProto::Ip4::e_im_inc_host: - u = streamIndex % data.src_ip_count(); - subnet = data.src_ip() & data.src_ip_mask(); - host = (((data.src_ip() & ~data.src_ip_mask()) + u) & - ~data.src_ip_mask()); - srcIp = subnet | host; - break; - case OstProto::Ip4::e_im_dec_host: - u = streamIndex % data.src_ip_count(); - subnet = data.src_ip() & data.src_ip_mask(); - host = (((data.src_ip() & ~data.src_ip_mask()) - u) & - ~data.src_ip_mask()); - srcIp = subnet | host; - break; - case OstProto::Ip4::e_im_random_host: - subnet = data.src_ip() & data.src_ip_mask(); - host = (qrand() & ~data.src_ip_mask()); - srcIp = subnet | host; - break; - default: - qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); - } + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } - switch(attrib) - { - case FieldName: - return QString("Source"); - case FieldValue: - return srcIp; - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian(srcIp, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QHostAddress(srcIp).toString(); - default: - break; - } - break; - } - case ip4_dstAddr: - { - int u; - quint32 subnet, host, dstIp = 0; + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; - switch(data.dst_ip_mode()) - { - case OstProto::Ip4::e_im_fixed: - dstIp = data.dst_ip(); - break; - case OstProto::Ip4::e_im_inc_host: - u = streamIndex % data.dst_ip_count(); - subnet = data.dst_ip() & data.dst_ip_mask(); - host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & - ~data.dst_ip_mask()); - dstIp = subnet | host; - break; - case OstProto::Ip4::e_im_dec_host: - u = streamIndex % data.dst_ip_count(); - subnet = data.dst_ip() & data.dst_ip_mask(); - host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & - ~data.dst_ip_mask()); - dstIp = subnet | host; - break; - case OstProto::Ip4::e_im_random_host: - subnet = data.dst_ip() & data.dst_ip_mask(); - host = (qrand() & ~data.dst_ip_mask()); - dstIp = subnet | host; - break; - default: - qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); - } + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } - switch(attrib) - { - case FieldName: - return QString("Destination"); - case FieldValue: - return dstIp; - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) dstIp, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QHostAddress(dstIp).toString(); - default: - break; - } - break; - } - // Meta fields + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields - case ip4_isOverrideVer: - case ip4_isOverrideHdrLen: - case ip4_isOverrideTotLen: - case ip4_isOverrideCksum: + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: - case ip4_srcAddrMode: - case ip4_srcAddrCount: - case ip4_srcAddrMask: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: - case ip4_dstAddrMode: - case ip4_dstAddrCount: - case ip4_dstAddrMask: - default: - break; - } + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Ip4Protocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - bool isOk = false; + bool isOk = false; - if (attrib != FieldValue) - return false; + if (attrib != FieldValue) + return false; - switch (index) - { - case ip4_proto: - { - uint proto = value.toUInt(&isOk); - if (isOk) - data.set_proto(proto); - } - default: - break; - } - return isOk; + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; } bool Ip4Protocol::isProtocolFrameValueVariable() const { - if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) - || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) - return true; - else - return false; + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; } quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType) const { - switch (cksumType) - { - case CksumIpPseudo: - { - quint32 sum; + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; - sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; - sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; - sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; - sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; - sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; - sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; - // Above calculation done assuming 'big endian' - // - so convert to host order - //return qFromBigEndian(sum); - return ~sum; - } - default: - break; - } + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } - return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } QWidget* Ip4Protocol::configWidget() { - if (configForm == NULL) - { - configForm = new Ip4ConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; } void Ip4Protocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); - configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); - configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); - configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); - - configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); - configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); - configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); - configForm->leIpId->setText(uintToHexStr(data.id(), 2)); - configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); - configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); - configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); - configForm->leIpTtl->setText(QString().setNum(data.ttl())); - configForm->leIpProto->setText(uintToHexStr( - fieldData(ip4_proto, FieldValue).toUInt(), 1)); + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); - configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); - configForm->leIpCksum->setText(uintToHexStr( - fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); - configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); - configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); - configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); - configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); - configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); - configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); - configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); - configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); } void Ip4Protocol::storeConfigWidget() { - uint ff = 0; - bool isOk; + uint ff = 0; + bool isOk; - configWidget(); + configWidget(); - data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); - data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | - (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); - data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); - data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); - data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); - data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); - data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); - data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); - if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; - if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; - data.set_flags(ff); + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); - data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); - data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); - - data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); - data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); - data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); - data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); - data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); - data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); - data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); - data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); - data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); - data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); } diff --git a/common/ip4.h b/common/ip4.h index e3e1a23..a9a40b9 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -6,89 +6,89 @@ #include "ip4.pb.h" #include "ui_ip4.h" -#define IP_FLAG_MF 0x1 -#define IP_FLAG_DF 0x2 -#define IP_FLAG_UNUSED 0x4 +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 class Ip4ConfigForm : public QWidget, public Ui::ip4 { - Q_OBJECT + Q_OBJECT public: - Ip4ConfigForm(QWidget *parent = 0); - ~Ip4ConfigForm(); + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); private slots: - void on_cmbIpSrcAddrMode_currentIndexChanged(int index); - void on_cmbIpDstAddrMode_currentIndexChanged(int index); + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); }; class Ip4Protocol : public AbstractProtocol { private: - OstProto::Ip4 data; - Ip4ConfigForm *configForm; - enum ip4field - { - ip4_ver = 0, - ip4_hdrLen, - ip4_tos, - ip4_totLen, - ip4_id, - ip4_flags, - ip4_fragOfs, - ip4_ttl, - ip4_proto, - ip4_cksum, - ip4_srcAddr, - ip4_dstAddr, + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, - ip4_isOverrideVer, - ip4_isOverrideHdrLen, - ip4_isOverrideTotLen, - ip4_isOverrideCksum, + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideCksum, - ip4_srcAddrMode, - ip4_srcAddrCount, - ip4_srcAddrMask, + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, - ip4_dstAddrMode, - ip4_dstAddrCount, - ip4_dstAddrMask, + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, - ip4_fieldCount - }; + ip4_fieldCount + }; public: - Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~Ip4Protocol(); + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; - virtual ProtocolIdType protocolIdType() const; - virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameValueVariable() const; - virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; diff --git a/common/ip4.proto b/common/ip4.proto index 84fcbf4..ffdfcae 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -4,43 +4,43 @@ package OstProto; // IPv4 message Ip4 { - enum IpAddrMode { - e_im_fixed = 0; - e_im_inc_host = 1; - e_im_dec_host = 2; - e_im_random_host = 3; - } + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } - optional bool is_override_ver = 1; - optional bool is_override_hdrlen = 2; - optional bool is_override_totlen = 3; - optional bool is_override_cksum = 4; + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; - optional uint32 ver_hdrlen = 5 [default = 0x45]; - optional uint32 tos = 6; - optional uint32 totlen = 7; - optional uint32 id = 8 [default = 1234]; - optional uint32 flags = 9; - optional uint32 frag_ofs = 10; - optional uint32 ttl = 11 [default = 127]; - optional uint32 proto = 12; - optional uint32 cksum = 13; + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; - // Source IP - optional fixed32 src_ip = 14; - optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; - optional uint32 src_ip_count = 16 [default = 16]; - optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; - - // Destination IP - optional fixed32 dst_ip = 18; - optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; - optional uint32 dst_ip_count = 20 [default = 16]; - optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; - //! \todo (LOW) IPv4 Options + //! \todo (LOW) IPv4 Options } extend Protocol { - optional Ip4 ip4 = 130; + optional Ip4 ip4 = 130; } diff --git a/common/llc.cpp b/common/llc.cpp index 25cc307..a4943f7 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -4,173 +4,173 @@ #include "llc.h" LlcConfigForm::LlcConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } LlcProtocol::~LlcProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new LlcProtocol(stream, parent); + return new LlcProtocol(stream, parent); } quint32 LlcProtocol::protocolNumber() const { - return OstProto::Protocol::kLlcFieldNumber; + return OstProto::Protocol::kLlcFieldNumber; } void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::llc)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::llc)) - data.MergeFrom(protocol.GetExtension(OstProto::llc)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); } QString LlcProtocol::name() const { - return QString("802.3 Logical Link Control"); + return QString("802.3 Logical Link Control"); } QString LlcProtocol::shortName() const { - return QString("LLC"); + return QString("LLC"); } AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const { - return ProtocolIdLlc; + return ProtocolIdLlc; } -int LlcProtocol::fieldCount() const +int LlcProtocol::fieldCount() const { - return llc_fieldCount; + return llc_fieldCount; } QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - quint32 id; - quint8 dsap, ssap, ctl; + quint32 id; + quint8 dsap, ssap, ctl; - id = payloadProtocolId(ProtocolIdLlc); - dsap = (id >> 16) & 0xFF; - ssap = (id >> 8) & 0xFF; - ctl = (id >> 0) & 0xFF; + id = payloadProtocolId(ProtocolIdLlc); + dsap = (id >> 16) & 0xFF; + ssap = (id >> 8) & 0xFF; + ctl = (id >> 0) & 0xFF; - switch (index) - { - case llc_dsap: - switch(attrib) - { - case FieldName: - return QString("DSAP"); - case FieldValue: - return dsap; - case FieldTextValue: - return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); - case FieldFrameValue: - return QByteArray(1, (char)(dsap)); - default: - break; - } - break; - case llc_ssap: - switch(attrib) - { - case FieldName: - return QString("SSAP"); - case FieldValue: - return ssap; - case FieldTextValue: - return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); - case FieldFrameValue: - return QByteArray(1, (char)(ssap)); - default: - break; - } - break; - case llc_ctl: - switch(attrib) - { - case FieldName: - return QString("Control"); - case FieldValue: - return ctl; - case FieldTextValue: - return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); - case FieldFrameValue: - return QByteArray(1, (char)(ctl)); - default: - break; - } - break; + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; - default: - break; - } + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool LlcProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } QWidget* LlcProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new LlcConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; } void LlcProtocol::loadConfigWidget() { -#define uintToHexStr(num, bytes) \ - QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) - configWidget(); + configWidget(); - configForm->leDsap->setText(uintToHexStr( - fieldData(llc_dsap, FieldValue).toUInt(), 1)); - configForm->leSsap->setText(uintToHexStr( - fieldData(llc_ssap, FieldValue).toUInt(), 1)); - configForm->leControl->setText(uintToHexStr( - fieldData(llc_ctl, FieldValue).toUInt(), 1)); + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); #undef uintToHexStr } void LlcProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); - data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); - data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); + data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); + data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); + data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); } diff --git a/common/llc.h b/common/llc.h index 741f493..5de0b72 100644 --- a/common/llc.h +++ b/common/llc.h @@ -11,51 +11,51 @@ class LlcConfigForm : public QWidget, public Ui::llc { - Q_OBJECT + Q_OBJECT public: - LlcConfigForm(QWidget *parent = 0); + LlcConfigForm(QWidget *parent = 0); }; class LlcProtocol : public AbstractProtocol { private: - OstProto::Llc data; - LlcConfigForm *configForm; - enum llcfield - { - llc_dsap = 0, - llc_ssap, - llc_ctl, + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, - llc_fieldCount - }; + llc_fieldCount + }; public: - LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~LlcProtocol(); + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual ProtocolIdType protocolIdType() const; + virtual ProtocolIdType protocolIdType() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/llc.proto b/common/llc.proto index 72a4393..c1966ab 100644 --- a/common/llc.proto +++ b/common/llc.proto @@ -3,11 +3,11 @@ import "protocol.proto"; package OstProto; message Llc { - optional uint32 dsap = 1; - optional uint32 ssap = 2; - optional uint32 ctl = 3; + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; } extend Protocol { - optional Llc llc = 123; + optional Llc llc = 123; } diff --git a/common/mac.cpp b/common/mac.cpp index 1d7ed87..efbd518 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -4,294 +4,294 @@ #include "mac.h" MacConfigForm::MacConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); - setupUi(this); - leDstMac->setValidator(new QRegExpValidator(reMac, this)); - leSrcMac->setValidator(new QRegExpValidator(reMac, this)); - leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); - leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); } MacConfigForm::~MacConfigForm() { - qDebug("In MacConfigForm destructor"); + qDebug("In MacConfigForm destructor"); } void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) { - if (index == OstProto::Mac::e_mm_fixed) - { - leDstMacCount->setEnabled(false); - leDstMacStep->setEnabled(false); - } - else - { - leDstMacCount->setEnabled(true); - leDstMacStep->setEnabled(true); - } + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } } void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) { - if (index == OstProto::Mac::e_mm_fixed) - { - leSrcMacCount->setEnabled(false); - leSrcMacStep->setEnabled(false); - } - else - { - leSrcMacCount->setEnabled(true); - leSrcMacStep->setEnabled(true); - } + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } } MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } MacProtocol::~MacProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* MacProtocol::createInstance(StreamBase *stream - , AbstractProtocol *parent) + , AbstractProtocol *parent) { - return new MacProtocol(stream, parent); + return new MacProtocol(stream, parent); } quint32 MacProtocol::protocolNumber() const { - return OstProto::Protocol::kMacFieldNumber; + return OstProto::Protocol::kMacFieldNumber; } void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::mac)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::mac)) - data.MergeFrom(protocol.GetExtension(OstProto::mac)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); } QString MacProtocol::name() const { - return QString("Media Access Protocol"); + return QString("Media Access Protocol"); } QString MacProtocol::shortName() const { - return QString("MAC"); + return QString("MAC"); } -int MacProtocol::fieldCount() const +int MacProtocol::fieldCount() const { - return mac_fieldCount; + return mac_fieldCount; } AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case mac_dstAddr: - case mac_srcAddr: - break; + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; - case mac_dstMacMode: - case mac_dstMacCount: - case mac_dstMacStep: - case mac_srcMacMode: - case mac_srcMacCount: - case mac_srcMacStep: - flags |= FieldIsMeta; - break; - } + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags |= FieldIsMeta; + break; + } - return flags; + return flags; } QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case mac_dstAddr: - { - int u; - quint64 dstMac = 0; + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; - switch (data.dst_mac_mode()) - { - case OstProto::Mac::e_mm_fixed: - dstMac = data.dst_mac(); - break; - case OstProto::Mac::e_mm_inc: - u = (streamIndex % data.dst_mac_count()) * - data.dst_mac_step(); - dstMac = data.dst_mac() + u; - break; - case OstProto::Mac::e_mm_dec: - u = (streamIndex % data.dst_mac_count()) * - data.dst_mac_step(); - dstMac = data.dst_mac() - u; - break; - default: - qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); - } + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } - switch(attrib) - { - case FieldName: - return QString("Desination"); - case FieldValue: - return dstMac; - case FieldTextValue: - return uintToHexStr(dstMac, 6); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(8); - qToBigEndian(dstMac, (uchar*) fv.data()); - fv.remove(0, 2); - return fv; - } - default: - break; - } - break; - } - case mac_srcAddr: - { - int u; - quint64 srcMac = 0; + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; - switch (data.src_mac_mode()) - { - case OstProto::Mac::e_mm_fixed: - srcMac = data.src_mac(); - break; - case OstProto::Mac::e_mm_inc: - u = (streamIndex % data.src_mac_count()) * - data.src_mac_step(); - srcMac = data.src_mac() + u; - break; - case OstProto::Mac::e_mm_dec: - u = (streamIndex % data.src_mac_count()) * - data.src_mac_step(); - srcMac = data.src_mac() - u; - break; - default: - qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); - } + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } - switch(attrib) - { - case FieldName: - return QString("Source"); - case FieldValue: - return srcMac; - case FieldTextValue: - return uintToHexStr(srcMac, 6); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(8); - qToBigEndian(srcMac, (uchar*) fv.data()); - fv.remove(0, 2); - return fv; - } - default: - break; - } - break; - } - // Meta fields - case mac_dstMacMode: - case mac_dstMacCount: - case mac_dstMacStep: - case mac_srcMacMode: - case mac_srcMacCount: - case mac_srcMacStep: - default: - break; - } + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool MacProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } bool MacProtocol::isProtocolFrameValueVariable() const { - if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || - (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) - return true; - else - return false; + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; } QWidget* MacProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new MacConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; } void MacProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); - configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); - configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); - configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); - configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); - configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); - configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); - configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); } void MacProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). - toULongLong(&isOk, 16)); - data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> - cmbDstMacMode->currentIndex()); - data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); - data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); - data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). - toULongLong(&isOk, 16)); - data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> - cmbSrcMacMode->currentIndex()); - data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); - data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); } diff --git a/common/mac.h b/common/mac.h index b6165b7..242ce2a 100644 --- a/common/mac.h +++ b/common/mac.h @@ -10,62 +10,62 @@ class MacConfigForm : public QWidget, public Ui::mac { - Q_OBJECT + Q_OBJECT public: - MacConfigForm(QWidget *parent = 0); - virtual ~MacConfigForm(); + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); private slots: - void on_cmbDstMacMode_currentIndexChanged(int index); - void on_cmbSrcMacMode_currentIndexChanged(int index); + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); }; class MacProtocol : public AbstractProtocol { private: - OstProto::Mac data; - MacConfigForm *configForm; - enum macfield - { - mac_dstAddr = 0, - mac_srcAddr, + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, - mac_dstMacMode, - mac_dstMacCount, - mac_dstMacStep, - mac_srcMacMode, - mac_srcMacCount, - mac_srcMacStep, + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, - mac_fieldCount - }; + mac_fieldCount + }; public: - MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~MacProtocol(); + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameValueVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/mac.proto b/common/mac.proto index a49c34e..642f725 100644 --- a/common/mac.proto +++ b/common/mac.proto @@ -5,25 +5,25 @@ package OstProto; // Ethernet message Mac { - enum MacAddrMode { - e_mm_fixed = 0; - e_mm_inc = 1; - e_mm_dec = 2; - } + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } - // Dst Mac - optional uint64 dst_mac = 1; - optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; - optional uint32 dst_mac_count = 3 [default = 16]; - optional uint32 dst_mac_step = 4 [default = 1]; + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; - // Src Mac - optional uint64 src_mac = 5; - optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; - optional uint32 src_mac_count = 7 [default = 16]; - optional uint32 src_mac_step = 8 [default = 1]; + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; } extend Protocol { - optional Mac mac = 51; + optional Mac mac = 51; } diff --git a/common/ostproto.pro b/common/ostproto.pro index 19121ca..b3c37bb 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -2,80 +2,80 @@ TEMPLATE = lib CONFIG += qt staticlib QT += network script LIBS += \ - -lprotobuf + -lprotobuf FORMS += \ - mac.ui \ - payload.ui \ - eth2.ui \ - dot3.ui \ - llc.ui \ - snap.ui \ - vlan.ui \ - ip4.ui \ - tcp.ui \ - udp.ui \ - userscript.ui \ - sample.ui + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + ip4.ui \ + tcp.ui \ + udp.ui \ + userscript.ui \ + sample.ui PROTOS += \ - protocol.proto \ - mac.proto \ - payload.proto \ - eth2.proto \ - dot3.proto \ - llc.proto \ - snap.proto \ - dot2llc.proto \ - dot2snap.proto \ - vlan.proto \ - svlan.proto \ - vlanstack.proto \ - ip4.proto \ - tcp.proto \ - udp.proto \ - userscript.proto \ - sample.proto + protocol.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + ip4.proto \ + tcp.proto \ + udp.proto \ + userscript.proto \ + sample.proto HEADERS += \ - abstractprotocol.h \ - comboprotocol.h \ - protocolmanager.h \ - protocollist.h \ - protocollistiterator.h \ - streambase.h \ - mac.h \ - payload.h \ - eth2.h \ - dot3.h \ - llc.h \ - snap.h \ - dot2llc.h \ - dot2snap.h \ - vlan.h \ - svlan.h \ - vlanstack.h \ - ip4.h \ - tcp.h \ - udp.h \ - userscript.h \ - sample.h + abstractprotocol.h \ + comboprotocol.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + ip4.h \ + tcp.h \ + udp.h \ + userscript.h \ + sample.h SOURCES += \ - abstractprotocol.cpp \ - protocolmanager.cpp \ - protocollist.cpp \ - protocollistiterator.cpp \ - streambase.cpp \ - mac.cpp \ - payload.cpp \ - eth2.cpp \ - dot3.cpp \ - llc.cpp \ - snap.cpp \ - vlan.cpp \ - svlan.cpp \ - ip4.cpp \ - tcp.cpp \ - udp.cpp \ - userscript.cpp \ - sample.cpp + abstractprotocol.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + ip4.cpp \ + tcp.cpp \ + udp.cpp \ + userscript.cpp \ + sample.cpp protobuf_decl.name = protobuf header protobuf_decl.input = PROTOS diff --git a/common/payload.cpp b/common/payload.cpp index 4bbca2e..276af95 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -5,232 +5,232 @@ #include "payload.h" #include "streambase.h" -#define SZ_FCS 4 +#define SZ_FCS 4 PayloadConfigForm::PayloadConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) { - switch(index) - { - case OstProto::Payload::e_dp_fixed_word: - lePattern->setEnabled(true); - break; - case OstProto::Payload::e_dp_inc_byte: - case OstProto::Payload::e_dp_dec_byte: - case OstProto::Payload::e_dp_random: - lePattern->setDisabled(true); - break; - default: - qWarning("Unhandled/Unknown PatternMode = %d",index); - } + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } } PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } PayloadProtocol::~PayloadProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new PayloadProtocol(stream, parent); + return new PayloadProtocol(stream, parent); } quint32 PayloadProtocol::protocolNumber() const { - return OstProto::Protocol::kPayloadFieldNumber; + return OstProto::Protocol::kPayloadFieldNumber; } void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::payload)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::payload)) - data.MergeFrom(protocol.GetExtension(OstProto::payload)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); } QString PayloadProtocol::name() const { - return QString("Payload Data"); + return QString("Payload Data"); } QString PayloadProtocol::shortName() const { - return QString("DATA"); + return QString("DATA"); } -int PayloadProtocol::protocolFrameSize(int streamIndex) const +int PayloadProtocol::protocolFrameSize(int streamIndex) const { - int len; + int len; - len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) - - SZ_FCS; + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - SZ_FCS; - qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, - streamIndex, len); - return len; + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; } -int PayloadProtocol::fieldCount() const +int PayloadProtocol::fieldCount() const { - return payload_fieldCount; + return payload_fieldCount; } AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case payload_dataPattern: - break; + switch (index) + { + case payload_dataPattern: + break; - // Meta fields - case payload_dataPatternMode: - flags |= FieldIsMeta; - break; - } + // Meta fields + case payload_dataPatternMode: + flags |= FieldIsMeta; + break; + } - return flags; + return flags; } QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case payload_dataPattern: - switch(attrib) - { - case FieldName: - return QString("Data"); - case FieldValue: - return data.pattern(); - case FieldTextValue: - return QString(fieldData(index, FieldFrameValue, - streamIndex).toByteArray().toHex()); - case FieldFrameValue: - { - QByteArray fv; - int dataLen; + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; - dataLen = protocolFrameSize(streamIndex); + dataLen = protocolFrameSize(streamIndex); - // FIXME: Hack! Bad! Bad! Very Bad!!! - if (dataLen <= 0) - dataLen = 1; + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; - fv.resize(dataLen+4); - switch(data.pattern_mode()) - { - case OstProto::Payload::e_dp_fixed_word: - for (int i = 0; i < (dataLen/4)+1; i++) - qToBigEndian((quint32) data.pattern(), - (uchar*)(fv.data()+(i*4)) ); - break; - case OstProto::Payload::e_dp_inc_byte: - for (int i = 0; i < dataLen; i++) - fv[i] = i % (0xFF + 1); - break; - case OstProto::Payload::e_dp_dec_byte: - for (int i = 0; i < dataLen; i++) - fv[i] = 0xFF - (i % (0xFF + 1)); - break; - case OstProto::Payload::e_dp_random: - //! \todo (HIGH) cksum is incorrect for random pattern - for (int i = 0; i < dataLen; i++) - fv[i] = qrand() % (0xFF + 1); - break; - default: - qWarning("Unhandled data pattern %d", - data.pattern_mode()); - } - fv.resize(dataLen); - return fv; - } - default: - break; - } - break; + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; - // Meta fields + // Meta fields - case payload_dataPatternMode: - default: - break; - } + case payload_dataPatternMode: + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool PayloadProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } bool PayloadProtocol::isProtocolFrameValueVariable() const { - if (isProtocolFrameSizeVariable() - || data.pattern_mode() == OstProto::Payload::e_dp_random) - return true; - else - return false; + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; } bool PayloadProtocol::isProtocolFrameSizeVariable() const { - if (mpStream->lenMode() == StreamBase::e_fl_fixed) - return false; - else - return true; + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; } QWidget* PayloadProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new PayloadConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; } void PayloadProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); - configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); } void PayloadProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_pattern_mode((OstProto::Payload::DataPatternMode) - configForm->cmbPatternMode->currentIndex()); - data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); } diff --git a/common/payload.h b/common/payload.h index 9437a8c..d468a16 100644 --- a/common/payload.h +++ b/common/payload.h @@ -8,58 +8,58 @@ class PayloadConfigForm : public QWidget, public Ui::payload { - Q_OBJECT + Q_OBJECT public: - PayloadConfigForm(QWidget *parent = 0); + PayloadConfigForm(QWidget *parent = 0); private slots: - void on_cmbPatternMode_currentIndexChanged(int index); + void on_cmbPatternMode_currentIndexChanged(int index); }; class PayloadProtocol : public AbstractProtocol { private: - OstProto::Payload data; - PayloadConfigForm *configForm; - enum payloadfield - { - payload_dataPattern, + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, - // Meta fields - payload_dataPatternMode, + // Meta fields + payload_dataPatternMode, - payload_fieldCount - }; + payload_fieldCount + }; public: - PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~PayloadProtocol(); + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual int protocolFrameSize(int streamIndex = 0) const; + virtual int protocolFrameSize(int streamIndex = 0) const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; - virtual bool isProtocolFrameSizeVariable() const; + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/payload.proto b/common/payload.proto index b3a6946..0cbc878 100644 --- a/common/payload.proto +++ b/common/payload.proto @@ -3,20 +3,20 @@ import "protocol.proto"; package OstProto; message Payload { - enum DataPatternMode { - e_dp_fixed_word = 0; - e_dp_inc_byte = 1; - e_dp_dec_byte = 2; - e_dp_random = 3; - } + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } - // Data Pattern - optional DataPatternMode pattern_mode = 1; - optional uint32 pattern = 2; + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; - //optional uint32 data_start_ofs = 13; + //optional uint32 data_start_ofs = 13; } extend Protocol { - optional Payload payload = 52; + optional Payload payload = 52; } diff --git a/common/protocol.proto b/common/protocol.proto index 30bec18..c8bdc05 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -3,208 +3,208 @@ package OstProto; message StreamId { - required uint32 id = 1; + required uint32 id = 1; } message StreamCore { - enum FrameLengthMode { - e_fl_fixed = 0; - e_fl_inc = 1; - e_fl_dec = 2; - e_fl_random = 3; - } - - // Basics - optional string name = 1; - optional bool is_enabled = 2; - optional uint32 ordinal = 3; + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; - // Frame Length (includes CRC) - optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; - optional uint32 frame_len = 15 [default = 64]; - optional uint32 frame_len_min = 16 [default = 64]; - optional uint32 frame_len_max = 17 [default = 1518]; + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; - // Currently Selected Protocols - //repeated uint32 frame_proto = 20; + // Currently Selected Protocols + //repeated uint32 frame_proto = 20; } message StreamControl { - enum SendUnit { - e_su_packets = 0; - e_su_bursts = 1; - } + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } - enum SendMode { - e_sm_fixed = 0; - e_sm_continuous = 1; - } + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } - enum NextWhat { - e_nw_stop = 0; - e_nw_goto_next = 1; - e_nw_goto_id = 2; - } + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } - optional SendUnit unit = 1 [default = e_su_packets]; - optional SendMode mode = 2 [default = e_sm_fixed]; - optional uint32 num_packets = 3 [default = 1]; - optional uint32 num_bursts = 4 [default = 1]; - optional uint32 packets_per_burst = 5 [default = 10]; - optional NextWhat next = 6 [default = e_nw_goto_next]; - optional uint32 packets_per_sec = 7 [default = 1]; - optional uint32 bursts_per_sec = 8 [default = 1]; + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; } message ProtocolId { - required uint32 id = 1; + required uint32 id = 1; } message Protocol { - required ProtocolId protocol_id = 1; + required ProtocolId protocol_id = 1; - extensions 51 to 100; // Reserved for Ostinato Use - extensions 101 to 200; // Available for use by protocols + extensions 51 to 100; // Reserved for Ostinato Use + extensions 101 to 200; // Available for use by protocols - enum k { - kMacFieldNumber = 51; - kPayloadFieldNumber = 52; - kSampleFieldNumber = 53; - kUserScriptFieldNumber = 54; + enum k { + kMacFieldNumber = 51; + kPayloadFieldNumber = 52; + kSampleFieldNumber = 53; + kUserScriptFieldNumber = 54; - kEth2FieldNumber = 121; - kDot3FieldNumber = 122; - kLlcFieldNumber = 123; - kSnapFieldNumber = 124; + kEth2FieldNumber = 121; + kDot3FieldNumber = 122; + kLlcFieldNumber = 123; + kSnapFieldNumber = 124; - kSvlanFieldNumber = 125; - kVlanFieldNumber = 126; + kSvlanFieldNumber = 125; + kVlanFieldNumber = 126; - kDot2LlcFieldNumber = 127; - kDot2SnapFieldNumber = 128; - kVlanStackFieldNumber = 129; + kDot2LlcFieldNumber = 127; + kDot2SnapFieldNumber = 128; + kVlanStackFieldNumber = 129; - kIp4FieldNumber = 130; - kArpFieldNumber = 131; + kIp4FieldNumber = 130; + kArpFieldNumber = 131; - kTcpFieldNumber = 140; - kUdpFieldNumber = 141; - kIcmpFieldNumber = 142; - kIgmpFieldNumber = 143; - } + kTcpFieldNumber = 140; + kUdpFieldNumber = 141; + kIcmpFieldNumber = 142; + kIgmpFieldNumber = 143; + } } message Stream { - required StreamId stream_id = 1; - optional StreamCore core = 2; - optional StreamControl control = 3; + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; - repeated Protocol protocol = 4; + repeated Protocol protocol = 4; } message Void { - // nothing! + // nothing! } message Ack { - //! \todo (LOW) do we need any fields in 'Ack' + //! \todo (LOW) do we need any fields in 'Ack' } message PortId { - required uint32 id = 1; + required uint32 id = 1; } message PortIdList { - repeated PortId port_id = 1; + repeated PortId port_id = 1; } message StreamIdList { - required PortId port_id = 1; - repeated StreamId stream_id = 2; + required PortId port_id = 1; + repeated StreamId stream_id = 2; } message Port { - required PortId port_id = 1; - optional string name = 2; - optional string description = 3; - optional bool is_enabled = 4; - optional bool is_exclusive_control = 6; + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional bool is_enabled = 4; + optional bool is_exclusive_control = 6; } message PortConfigList { - repeated Port port = 1; + repeated Port port = 1; } message StreamConfigList { - required PortId port_id = 1; - repeated Stream stream = 2; + required PortId port_id = 1; + repeated Stream stream = 2; } message CaptureBuffer { - //! \todo (HIGH) define CaptureBuffer + //! \todo (HIGH) define CaptureBuffer } message CaptureBufferList { - repeated CaptureBuffer list = 1; + repeated CaptureBuffer list = 1; } enum LinkState { - LinkStateUnknown = 0; - LinkStateDown = 1; - LinkStateUp = 2; + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; } message PortState { - optional LinkState link_state = 1 [default = LinkStateUnknown]; - optional bool is_transmit_on = 2 [default = false]; - optional bool is_capture_on = 3 [default = false]; + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; } message PortStats { - required PortId port_id = 1; + required PortId port_id = 1; - optional PortState state = 2; + optional PortState state = 2; - optional uint64 rx_pkts = 11; - optional uint64 rx_bytes = 12; - optional uint64 rx_pkts_nic = 13; - optional uint64 rx_bytes_nic = 14; - optional uint64 rx_pps = 15; - optional uint64 rx_bps = 16; + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; - optional uint64 tx_pkts = 21; - optional uint64 tx_bytes = 22; - optional uint64 tx_pkts_nic = 23; - optional uint64 tx_bytes_nic = 24; - optional uint64 tx_pps = 25; - optional uint64 tx_bps = 26; + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; } message PortStatsList { - repeated PortStats port_stats = 1; + repeated PortStats port_stats = 1; } service OstService { - rpc getPortIdList(Void) returns (PortIdList); - rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); - rpc getStreamIdList(PortId) returns (StreamIdList); - rpc getStreamConfig(StreamIdList) returns (StreamConfigList); - rpc addStream(StreamIdList) returns (Ack); - rpc deleteStream(StreamIdList) returns (Ack); - rpc modifyStream(StreamConfigList) returns (Ack); + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); - rpc startTx(PortIdList) returns (Ack); - rpc stopTx(PortIdList) returns (Ack); + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); - rpc startCapture(PortIdList) returns (Ack); - rpc stopCapture(PortIdList) returns (Ack); - rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); - rpc getStats(PortIdList) returns (PortStatsList); - rpc clearStats(PortIdList) returns (Ack); + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); } diff --git a/common/protocollist.cpp b/common/protocollist.cpp index 26d2aab..08ff40a 100644 --- a/common/protocollist.cpp +++ b/common/protocollist.cpp @@ -3,6 +3,6 @@ void ProtocolList::destroy() { - while (!isEmpty()) - delete takeFirst(); + while (!isEmpty()) + delete takeFirst(); } diff --git a/common/protocollist.h b/common/protocollist.h index bbf2720..d760f14 100644 --- a/common/protocollist.h +++ b/common/protocollist.h @@ -5,5 +5,5 @@ class AbstractProtocol; class ProtocolList : public QLinkedList { public: - void destroy(); + void destroy(); }; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp index 268e2d6..242d8d9 100644 --- a/common/protocollistiterator.cpp +++ b/common/protocollistiterator.cpp @@ -4,111 +4,111 @@ ProtocolListIterator::ProtocolListIterator(ProtocolList &list) { - _iter = new QMutableLinkedListIterator(list); + _iter = new QMutableLinkedListIterator(list); } ProtocolListIterator::~ProtocolListIterator() { - delete _iter; + delete _iter; } bool ProtocolListIterator::findNext(const AbstractProtocol* value) const { - return _iter->findNext((AbstractProtocol*)((uint)value)); + return _iter->findNext((AbstractProtocol*)((uint)value)); } bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) { - return _iter->findPrevious((AbstractProtocol*)((uint)value)); + return _iter->findPrevious((AbstractProtocol*)((uint)value)); } bool ProtocolListIterator::hasNext() const { - return _iter->hasNext(); + return _iter->hasNext(); } bool ProtocolListIterator::hasPrevious() const { - return _iter->hasPrevious(); + return _iter->hasPrevious(); } void ProtocolListIterator::insert(AbstractProtocol* value) { - if (_iter->hasPrevious()) - { - value->prev = _iter->peekPrevious(); - value->prev->next = value; - } - else - value->prev = NULL; + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; - if (_iter->hasNext()) - { - value->next = _iter->peekNext(); - value->next->prev = value; - } - else - value->next = NULL; + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; - _iter->insert((AbstractProtocol*)((uint)value)); + _iter->insert((AbstractProtocol*)((uint)value)); } AbstractProtocol* ProtocolListIterator::next() { - return _iter->next(); + return _iter->next(); } AbstractProtocol* ProtocolListIterator::peekNext() const { - return _iter->peekNext(); + return _iter->peekNext(); } AbstractProtocol* ProtocolListIterator::peekPrevious() const { - return _iter->peekPrevious(); + return _iter->peekPrevious(); } AbstractProtocol* ProtocolListIterator::previous() { - return _iter->previous(); + return _iter->previous(); } void ProtocolListIterator::remove() { - if (_iter->value()->prev) - _iter->value()->prev->next = _iter->value()->next; - if (_iter->value()->next) - _iter->value()->next->prev = _iter->value()->prev; - _iter->remove(); + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); } void ProtocolListIterator::setValue(AbstractProtocol* value) const { - if (_iter->value()->prev) - _iter->value()->prev->next = value; - if (_iter->value()->next) - _iter->value()->next->prev = value; - value->prev = _iter->value()->prev; - value->next = _iter->value()->next; - _iter->setValue((AbstractProtocol*)((uint)value)); + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue((AbstractProtocol*)((uint)value)); } void ProtocolListIterator::toBack() { - _iter->toBack(); + _iter->toBack(); } void ProtocolListIterator::toFront() { - _iter->toFront(); + _iter->toFront(); } const AbstractProtocol* ProtocolListIterator::value() const { - return _iter->value(); + return _iter->value(); } AbstractProtocol* ProtocolListIterator::value() { - return _iter->value(); + return _iter->value(); } diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h index 8ad4168..463dae9 100644 --- a/common/protocollistiterator.h +++ b/common/protocollistiterator.h @@ -6,24 +6,24 @@ class ProtocolList; class ProtocolListIterator { private: - QMutableLinkedListIterator *_iter; + QMutableLinkedListIterator *_iter; public: - ProtocolListIterator(ProtocolList &list); - ~ProtocolListIterator(); - bool findNext(const AbstractProtocol* value) const; - bool findPrevious(const AbstractProtocol* value); - bool hasNext() const; - bool hasPrevious() const; - void insert(AbstractProtocol* value); - AbstractProtocol* next(); - AbstractProtocol* peekNext() const; - AbstractProtocol* peekPrevious() const; - AbstractProtocol* previous(); - void remove(); - void setValue(AbstractProtocol* value) const; - void toBack(); - void toFront(); - const AbstractProtocol* value() const; - AbstractProtocol* value(); + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); }; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 7c8763d..eb948ba 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -4,17 +4,17 @@ #include "protocol.pb.h" #include "mac.h" #include "payload.h" -#include "eth2.h" -#include "dot3.h" -#include "llc.h" -#include "snap.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" #include "dot2llc.h" #include "dot2snap.h" -#include "vlan.h" -#include "vlanstack.h" -#include "ip4.h" -#include "tcp.h" -#include "udp.h" +#include "vlan.h" +#include "vlanstack.h" +#include "ip4.h" +#include "tcp.h" +#include "udp.h" #include "userscript.h" #include "sample.h" @@ -22,111 +22,111 @@ ProtocolManager OstProtocolManager; ProtocolManager::ProtocolManager() { - /*! \todo (LOW) calls to registerProtocol() should be done by the protocols - themselves (once this is done remove the #includes for all the protocols) - */ - registerProtocol(OstProto::Protocol::kMacFieldNumber, - (void*) MacProtocol::createInstance); - registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadProtocol::createInstance); - registerProtocol(OstProto::Protocol::kEth2FieldNumber, - (void*) Eth2Protocol::createInstance); - registerProtocol(OstProto::Protocol::kDot3FieldNumber, - (void*) Dot3Protocol::createInstance); - registerProtocol(OstProto::Protocol::kLlcFieldNumber, - (void*) LlcProtocol::createInstance); - registerProtocol(OstProto::Protocol::kSnapFieldNumber, - (void*) SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, - (void*) Dot2LlcProtocol::createInstance); - registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, - (void*) Dot2SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kSvlanFieldNumber, - (void*) SVlanProtocol::createInstance); - registerProtocol(OstProto::Protocol::kVlanFieldNumber, - (void*) VlanProtocol::createInstance); - registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, - (void*) VlanStackProtocol::createInstance); - registerProtocol(OstProto::Protocol::kIp4FieldNumber, - (void*) Ip4Protocol::createInstance); - registerProtocol(OstProto::Protocol::kTcpFieldNumber, - (void*) TcpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kUdpFieldNumber, - (void*) UdpProtocol::createInstance); + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, - (void*) UserScriptProtocol::createInstance); - registerProtocol(OstProto::Protocol::kSampleFieldNumber, - (void*) SampleProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); - populateNeighbourProtocols(); + populateNeighbourProtocols(); } void ProtocolManager::registerProtocol(int protoNumber, - void *protoInstanceCreator) + void *protoInstanceCreator) { - AbstractProtocol *p; + AbstractProtocol *p; - Q_ASSERT(!factory.contains(protoNumber)); + Q_ASSERT(!factory.contains(protoNumber)); - factory.insert(protoNumber, protoInstanceCreator); + factory.insert(protoNumber, protoInstanceCreator); - p = createProtocol(protoNumber, NULL); - protocolList.append(p); + p = createProtocol(protoNumber, NULL); + protocolList.append(p); - numberToNameMap.insert(protoNumber, p->shortName()); - nameToNumberMap.insert(p->shortName(), protoNumber); + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); } void ProtocolManager::populateNeighbourProtocols() { - neighbourProtocols.clear(); + neighbourProtocols.clear(); - foreach(AbstractProtocol *p, protocolList) - { - if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) - { - foreach(AbstractProtocol *q, protocolList) - { - if (q->protocolId(p->protocolIdType())) - neighbourProtocols.insert( - p->protocolNumber(), q->protocolNumber()); - } - } - } + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } } AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, - StreamBase *stream, AbstractProtocol *parent) + StreamBase *stream, AbstractProtocol *parent) { - AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); - AbstractProtocol* p; + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; - pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) - factory.value(protoNumber); - - Q_ASSERT(pc != NULL); + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); - p = (*pc)(stream, parent); + p = (*pc)(stream, parent); - return p; + return p; } AbstractProtocol* ProtocolManager::createProtocol(QString protoName, - StreamBase *stream, AbstractProtocol *parent) + StreamBase *stream, AbstractProtocol *parent) { - return createProtocol(nameToNumberMap.value(protoName), stream, parent); + return createProtocol(nameToNumberMap.value(protoName), stream, parent); } bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) { - if (neighbourProtocols.contains(protoPrefix, protoSuffix)) - return true; - else - return false; + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; } QStringList ProtocolManager::protocolDatabase() { - return numberToNameMap.values(); + return numberToNameMap.values(); } diff --git a/common/protocolmanager.h b/common/protocolmanager.h index c619099..86ccf5d 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -9,27 +9,27 @@ class StreamBase; class ProtocolManager { - QMap numberToNameMap; - QMap nameToNumberMap; - QMultiMap neighbourProtocols; - QMap factory; - QList protocolList; + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QList protocolList; - void populateNeighbourProtocols(); + void populateNeighbourProtocols(); public: - ProtocolManager(); + ProtocolManager(); - void registerProtocol(int protoNumber, void *protoInstanceCreator); + void registerProtocol(int protoNumber, void *protoInstanceCreator); - AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, - AbstractProtocol *parent = 0); - AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, - AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); - bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool isValidNeighbour(int protoPrefix, int protoSuffix); - QStringList protocolDatabase(); + QStringList protocolDatabase(); }; #endif diff --git a/common/sample.cpp b/common/sample.cpp index 2477908..00a65e6 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -3,54 +3,54 @@ #include "sample.h" SampleConfigForm::SampleConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } SampleProtocol::~SampleProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new SampleProtocol(stream, parent); + return new SampleProtocol(stream, parent); } quint32 SampleProtocol::protocolNumber() const { - return OstProto::Protocol::kSampleFieldNumber; + return OstProto::Protocol::kSampleFieldNumber; } void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::sample)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::sample)) - data.MergeFrom(protocol.GetExtension(OstProto::sample)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); } QString SampleProtocol::name() const { - return QString("Sample Protocol"); + return QString("Sample Protocol"); } QString SampleProtocol::shortName() const { - return QString("SAMPLE"); + return QString("SAMPLE"); } /*! @@ -62,7 +62,7 @@ QString SampleProtocol::shortName() const */ AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const { - return ProtocolIdIp; + return ProtocolIdIp; } /*! @@ -75,314 +75,314 @@ AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const */ quint32 SampleProtocol::protocolId(ProtocolIdType type) const { - switch(type) - { - case ProtocolIdIp: return 1234; - default:break; - } + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } - return AbstractProtocol::protocolId(type); + return AbstractProtocol::protocolId(type); } -int SampleProtocol::fieldCount() const +int SampleProtocol::fieldCount() const { - return sample_fieldCount; + return sample_fieldCount; } AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case sample_a: - case sample_b: - case sample_payloadLength: - break; + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; - case sample_checksum: - flags |= FieldIsCksum; - break; + case sample_checksum: + flags |= FieldIsCksum; + break; - case sample_x: - case sample_y: - break; + case sample_x: + case sample_y: + break; - case sample_is_override_checksum: - flags |= FieldIsMeta; - break; + case sample_is_override_checksum: + flags |= FieldIsMeta; + break; - default: - qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, - index); - break; - } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } - return flags; + return flags; } QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case sample_a: - { - int a = data.ab() >> 13; + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; - switch(attrib) - { - case FieldName: - return QString("A"); - case FieldValue: - return a; - case FieldTextValue: - return QString("%1").arg(a); - case FieldFrameValue: - return QByteArray(1, (char) a); - case FieldBitSize: - return 3; - default: - break; - } - break; + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; - } - case sample_b: - { - int b = data.ab() & 0x1FFF; + } + case sample_b: + { + int b = data.ab() & 0x1FFF; - switch(attrib) - { - case FieldName: - return QString("B"); - case FieldValue: - return b; - case FieldTextValue: - return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) b, (uchar*) fv.data()); - return fv; - } - case FieldBitSize: - return 13; - default: - break; - } - break; - } + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } - case sample_payloadLength: - { - switch(attrib) - { - case FieldName: - return QString("Payload Length"); - case FieldValue: - return protocolFramePayloadSize(streamIndex); - case FieldFrameValue: - { - QByteArray fv; - int totlen; - totlen = protocolFramePayloadSize(streamIndex); - fv.resize(2); - qToBigEndian((quint16) totlen, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QString("%1").arg( - protocolFramePayloadSize(streamIndex)); - case FieldBitSize: - return 16; - default: - break; - } - break; - } - case sample_checksum: - { - quint16 cksum; + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; - switch(attrib) - { - case FieldValue: - case FieldFrameValue: - case FieldTextValue: - { - if (data.is_override_checksum()) - cksum = data.checksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumIp); - } - default: - break; - } + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + } + default: + break; + } - switch(attrib) - { - case FieldName: - return QString("Checksum"); - case FieldValue: - return cksum; - case FieldFrameValue: - { - QByteArray fv; + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; - fv.resize(2); - qToBigEndian(cksum, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QString("0x%1").arg( - cksum, 4, BASE_HEX, QChar('0'));; - case FieldBitSize: - return 16; - default: - break; - } - break; - } - case sample_x: - { - switch(attrib) - { - case FieldName: - return QString("X"); - case FieldValue: - return data.x(); - case FieldTextValue: - return QString("%1").arg(data.x()); - //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.x(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; - } - case sample_y: - { - switch(attrib) - { - case FieldName: - return QString("Y"); - case FieldValue: - return data.y(); - case FieldTextValue: - //return QString("%1").arg(data.y()); - return QString("%1").arg(data.y(), 8, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.y(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; - } + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + return QString("%1").arg(data.x()); + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + //return QString("%1").arg(data.y()); + return QString("%1").arg(data.y(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } - // Meta fields - case sample_is_override_checksum: - { - switch(attrib) - { - case FieldValue: - return data.is_override_checksum(); - default: - break; - } - break; - } - default: - qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, - index); - break; - } + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool SampleProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - bool isOk = false; + bool isOk = false; - if (attrib != FieldValue) - goto _exit; + if (attrib != FieldValue) + goto _exit; - switch (index) - { - case sample_a: - { - uint a = value.toUInt(&isOk); - if (isOk) - data.set_ab((data.ab() & 0xe000) | (a << 13)); - break; - } - case sample_b: - { - uint b = value.toUInt(&isOk); - if (isOk) - data.set_ab((data.ab() & 0x1FFF) | b); - break; - } - case sample_payloadLength: - { - uint len = value.toUInt(&isOk); - if (isOk) - data.set_payload_length(len); - break; - } - case sample_checksum: - { - uint csum = value.toUInt(&isOk); - if (isOk) - data.set_checksum(csum); - break; - } - case sample_x: - { - uint x = value.toUInt(&isOk); - if (isOk) - data.set_x(x); - break; - } - case sample_y: - { - uint y = value.toUInt(&isOk); - if (isOk) - data.set_y(y); - break; - } - case sample_is_override_checksum: - { - bool ovr = value.toBool(); - data.set_is_override_checksum(ovr); - isOk = true; - break; - } - default: - qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, - index); - break; - } + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } _exit: - return isOk; + return isOk; } /*! @@ -393,7 +393,7 @@ _exit: */ int SampleProtocol::protocolFrameSize(int streamIndex) const { - return AbstractProtocol::protocolFrameSize(streamIndex); + return AbstractProtocol::protocolFrameSize(streamIndex); } /*! @@ -404,7 +404,7 @@ int SampleProtocol::protocolFrameSize(int streamIndex) const */ bool SampleProtocol::isProtocolFrameValueVariable() const { - return false; + return false; } /*! @@ -416,54 +416,54 @@ bool SampleProtocol::isProtocolFrameValueVariable() const */ bool SampleProtocol::isProtocolFrameSizeVariable() const { - return false; + return false; } QWidget* SampleProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new SampleConfigForm; - loadConfigWidget(); - } + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } - return configForm; + return configForm; } void SampleProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); - configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); - configForm->samplePayloadLength->setText( - fieldData(sample_payloadLength, FieldValue).toString()); + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); - configForm->isChecksumOverride->setChecked( - fieldData(sample_is_override_checksum, FieldValue).toBool()); - configForm->sampleChecksum->setText(uintToHexStr( - fieldData(sample_checksum, FieldValue).toUInt(), 2)); + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); - configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); - configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); } void SampleProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); - setFieldData(sample_a, configForm->sampleA->text()); - setFieldData(sample_b, configForm->sampleB->text()); + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); - setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); - setFieldData(sample_is_override_checksum, - configForm->isChecksumOverride->isChecked()); - setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); - setFieldData(sample_x, configForm->sampleX->text()); - setFieldData(sample_y, configForm->sampleY->text()); + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); } diff --git a/common/sample.h b/common/sample.h index a0e0ad3..55f549e 100644 --- a/common/sample.h +++ b/common/sample.h @@ -9,74 +9,74 @@ /* Sample Protocol Frame Format - +-----+------+------+------+------+------+ - | A | B | LEN | CSUM | X | Y | - | (3) | (13) | (16) | (16) | (32) | (32) | + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | +-----+------+------+------+------+------+ Figures in brackets represent field width in bits */ class SampleConfigForm : public QWidget, public Ui::Sample { - Q_OBJECT + Q_OBJECT public: - SampleConfigForm(QWidget *parent = 0); + SampleConfigForm(QWidget *parent = 0); private slots: }; class SampleProtocol : public AbstractProtocol { private: - OstProto::Sample data; - SampleConfigForm *configForm; - enum samplefield - { - // Frame Fields - sample_a = 0, - sample_b, - sample_payloadLength, - sample_checksum, - sample_x, - sample_y, + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, - // Meta Fields - sample_is_override_checksum, + // Meta Fields + sample_is_override_checksum, - sample_fieldCount - }; + sample_fieldCount + }; public: - SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~SampleProtocol(); + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual ProtocolIdType protocolIdType() const; - virtual quint32 protocolId(ProtocolIdType type) const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual int protocolFrameSize(int streamIndex = 0) const; + virtual int protocolFrameSize(int streamIndex = 0) const; - virtual bool isProtocolFrameValueVariable() const; - virtual bool isProtocolFrameSizeVariable() const; + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/sample.proto b/common/sample.proto index 76213d4..16d4daf 100644 --- a/common/sample.proto +++ b/common/sample.proto @@ -5,15 +5,15 @@ package OstProto; // Sample Protocol message Sample { - optional bool is_override_checksum = 1; + optional bool is_override_checksum = 1; - optional uint32 ab = 2; - optional uint32 payload_length = 3; - optional uint32 checksum = 4; - optional uint32 x = 5 [default = 1234]; - optional uint32 y = 6; + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; } extend Protocol { - optional Sample sample = 53; + optional Sample sample = 53; } diff --git a/common/snap.cpp b/common/snap.cpp index 0f7d477..3eaab91 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -4,171 +4,171 @@ #include "snap.h" SnapConfigForm::SnapConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } SnapProtocol::~SnapProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new SnapProtocol(stream, parent); + return new SnapProtocol(stream, parent); } quint32 SnapProtocol::protocolNumber() const { - return OstProto::Protocol::kSnapFieldNumber; + return OstProto::Protocol::kSnapFieldNumber; } void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::snap)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::snap)) - data.MergeFrom(protocol.GetExtension(OstProto::snap)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); } QString SnapProtocol::name() const { - return QString("SubNetwork Access Protocol"); + return QString("SubNetwork Access Protocol"); } QString SnapProtocol::shortName() const { - return QString("SNAP"); + return QString("SNAP"); } AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const { - return ProtocolIdEth; + return ProtocolIdEth; } quint32 SnapProtocol::protocolId(ProtocolIdType type) const { - switch(type) - { - case ProtocolIdLlc: return 0xAAAA03; - default: break; - } + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } - return AbstractProtocol::protocolId(type); + return AbstractProtocol::protocolId(type); } -int SnapProtocol::fieldCount() const +int SnapProtocol::fieldCount() const { - return snap_fieldCount; + return snap_fieldCount; } QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case snap_oui: - switch(attrib) - { - case FieldName: - return QString("OUI"); - case FieldValue: - return data.oui(); - case FieldTextValue: - return QString("%1").arg(data.oui(), 6, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.oui(), (uchar*) fv.data()); - fv.remove(0, 1); - return fv; - } - default: - break; - } - break; - case snap_type: - { - quint16 type; + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + return data.oui(); + case FieldTextValue: + return QString("%1").arg(data.oui(), 6, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.oui(), (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; - switch(attrib) - { - case FieldName: - return QString("Type"); - case FieldValue: - type = payloadProtocolId(ProtocolIdEth); - return type; - case FieldTextValue: - type = payloadProtocolId(ProtocolIdEth); - return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - type = payloadProtocolId(ProtocolIdEth); - qToBigEndian(type, (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; - } - default: - break; - } + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool SnapProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } QWidget* SnapProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new SnapConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; } void SnapProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leOui->setText(uintToHexStr( - fieldData(snap_oui, FieldValue).toUInt(), 3)); - configForm->leType->setText(uintToHexStr( - fieldData(snap_type, FieldValue).toUInt(), 2)); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); } void SnapProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); - data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); + data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); + data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); } diff --git a/common/snap.h b/common/snap.h index c4018ad..42eae0c 100644 --- a/common/snap.h +++ b/common/snap.h @@ -8,51 +8,51 @@ class SnapConfigForm : public QWidget, public Ui::snap { - Q_OBJECT + Q_OBJECT public: - SnapConfigForm(QWidget *parent = 0); + SnapConfigForm(QWidget *parent = 0); }; class SnapProtocol : public AbstractProtocol { private: - OstProto::Snap data; - SnapConfigForm *configForm; - enum snapfield - { - snap_oui = 0, - snap_type, + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, - snap_fieldCount - }; + snap_fieldCount + }; public: - SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~SnapProtocol(); + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual ProtocolIdType protocolIdType() const; - virtual quint32 protocolId(ProtocolIdType type) const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/snap.proto b/common/snap.proto index 957c213..7ac3254 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -3,10 +3,10 @@ import "protocol.proto"; package OstProto; message Snap { - optional uint32 oui = 1; - optional uint32 type = 2; + optional uint32 oui = 1; + optional uint32 type = 2; } extend Protocol { - optional Snap snap = 124; + optional Snap snap = 124; } diff --git a/common/streambase.cpp b/common/streambase.cpp index 5bd2dcb..e40f009 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -7,373 +7,373 @@ extern ProtocolManager OstProtocolManager; StreamBase::StreamBase() : - mStreamId(new OstProto::StreamId), - mCore(new OstProto::StreamCore), - mControl(new OstProto::StreamControl) + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) { - AbstractProtocol *proto; - ProtocolListIterator *iter; + AbstractProtocol *proto; + ProtocolListIterator *iter; - mStreamId->set_id(0xFFFFFFFF); + mStreamId->set_id(0xFFFFFFFF); - currentFrameProtocols = new ProtocolList; + currentFrameProtocols = new ProtocolList; - iter = createProtocolListIterator(); - // By default newly created streams have the mac and payload protocols - proto = OstProtocolManager.createProtocol( - OstProto::Protocol::kMacFieldNumber, this); - iter->insert(proto); - qDebug("stream: mac = %p", proto); + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager.createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); - proto = OstProtocolManager.createProtocol( - OstProto::Protocol::kPayloadFieldNumber, this); - iter->insert(proto); - qDebug("stream: payload = %p", proto); + proto = OstProtocolManager.createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); - { - iter->toFront(); - while (iter->hasNext()) - { - qDebug("{{%p}}", iter->next()); - // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); - } - iter->toFront(); - while (iter->hasNext()) - { - qDebug("{[%d]}", iter->next()->protocolNumber()); - // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); - } - } + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } - delete iter; + delete iter; } StreamBase::~StreamBase() { - delete mStreamId; - delete mCore; - delete mControl; + delete mStreamId; + delete mCore; + delete mControl; } void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) { - AbstractProtocol *proto; - ProtocolListIterator *iter; + AbstractProtocol *proto; + ProtocolListIterator *iter; - mStreamId->CopyFrom(stream.stream_id()); - mCore->CopyFrom(stream.core()); - mControl->CopyFrom(stream.control()); + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); - currentFrameProtocols->destroy(); - iter = createProtocolListIterator(); - for (int i=0; i < stream.protocol_size(); i++) - { - proto = OstProtocolManager.createProtocol( - stream.protocol(i).protocol_id().id(), this); - proto->protoDataCopyFrom(stream.protocol(i)); - iter->insert(proto); - } + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + proto = OstProtocolManager.createProtocol( + stream.protocol(i).protocol_id().id(), this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } - delete iter; + delete iter; } void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const { - stream.mutable_stream_id()->CopyFrom(*mStreamId); - stream.mutable_core()->CopyFrom(*mCore); - stream.mutable_control()->CopyFrom(*mControl); + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); - stream.clear_protocol(); - foreach (const AbstractProtocol* proto, *currentFrameProtocols) - { - OstProto::Protocol *p; + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; - p = stream.add_protocol(); - proto->protoDataCopyInto(*p); - } + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } } #if 0 ProtocolList StreamBase::frameProtocol() { - return currentFrameProtocols; + return currentFrameProtocols; } void StreamBase::setFrameProtocol(ProtocolList protocolList) { - //currentFrameProtocols.destroy(); - currentFrameProtocols = protocolList; + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; } #endif ProtocolListIterator* StreamBase::createProtocolListIterator() const { - return new ProtocolListIterator(*currentFrameProtocols); + return new ProtocolListIterator(*currentFrameProtocols); } bool StreamBase::operator < (const StreamBase &s) const { - return(mCore->ordinal() < s.mCore->ordinal()); + return(mCore->ordinal() < s.mCore->ordinal()); } -quint32 StreamBase::id() +quint32 StreamBase::id() { - return mStreamId->id(); + return mStreamId->id(); } bool StreamBase::setId(quint32 id) { - mStreamId->set_id(id); - return true; + mStreamId->set_id(id); + return true; } -quint32 StreamBase::ordinal() +quint32 StreamBase::ordinal() { - return mCore->ordinal(); + return mCore->ordinal(); } -bool StreamBase::setOrdinal(quint32 ordinal) +bool StreamBase::setOrdinal(quint32 ordinal) { - mCore->set_ordinal(ordinal); - return true; + mCore->set_ordinal(ordinal); + return true; } bool StreamBase::isEnabled() const { - return mCore->is_enabled(); + return mCore->is_enabled(); } bool StreamBase::setEnabled(bool flag) { - mCore->set_is_enabled(flag); - return true; + mCore->set_is_enabled(flag); + return true; } const QString StreamBase::name() const { - return QString().fromStdString(mCore->name()); + return QString().fromStdString(mCore->name()); } bool StreamBase::setName(QString name) { - mCore->set_name(name.toStdString()); - return true; + mCore->set_name(name.toStdString()); + return true; } -StreamBase::FrameLengthMode StreamBase::lenMode() const +StreamBase::FrameLengthMode StreamBase::lenMode() const { - return (StreamBase::FrameLengthMode) mCore->len_mode(); + return (StreamBase::FrameLengthMode) mCore->len_mode(); } -bool StreamBase::setLenMode(FrameLengthMode lenMode) +bool StreamBase::setLenMode(FrameLengthMode lenMode) { - mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); - return true; + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; } -quint16 StreamBase::frameLen(int streamIndex) const +quint16 StreamBase::frameLen(int streamIndex) const { - int pktLen; + int pktLen; - // Decide a frame length based on length mode - switch(lenMode()) - { - case OstProto::StreamCore::e_fl_fixed: - pktLen = mCore->frame_len(); - break; - case OstProto::StreamCore::e_fl_inc: - pktLen = frameLenMin() + (streamIndex % - (frameLenMax() - frameLenMin() + 1)); - break; - case OstProto::StreamCore::e_fl_dec: - pktLen = frameLenMax() - (streamIndex % - (frameLenMax() - frameLenMin() + 1)); - break; - case OstProto::StreamCore::e_fl_random: - //! \todo (MED) This 'random' sequence is same across iterations - qsrand(((uint) this)); - for (int i = 0; i <= streamIndex; i++) - pktLen = qrand(); - pktLen = frameLenMin() + (pktLen % - (frameLenMax() - frameLenMin() + 1)); - break; - default: - qWarning("Unhandled len mode %d. Using default 64", - lenMode()); - pktLen = 64; - break; - } + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + qsrand(((uint) this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } - return pktLen; + return pktLen; } bool StreamBase::setFrameLen(quint16 frameLen) { - mCore->set_frame_len(frameLen); - return true; + mCore->set_frame_len(frameLen); + return true; } -quint16 StreamBase::frameLenMin() const +quint16 StreamBase::frameLenMin() const { - return mCore->frame_len_min(); + return mCore->frame_len_min(); } bool StreamBase::setFrameLenMin(quint16 frameLenMin) { - mCore->set_frame_len_min(frameLenMin); - return true; + mCore->set_frame_len_min(frameLenMin); + return true; } -quint16 StreamBase::frameLenMax() const +quint16 StreamBase::frameLenMax() const { - return mCore->frame_len_max(); + return mCore->frame_len_max(); } bool StreamBase::setFrameLenMax(quint16 frameLenMax) { - mCore->set_frame_len_max(frameLenMax); - return true; + mCore->set_frame_len_max(frameLenMax); + return true; } StreamBase::SendUnit StreamBase::sendUnit() const { - return (StreamBase::SendUnit) mControl->unit(); + return (StreamBase::SendUnit) mControl->unit(); } bool StreamBase::setSendUnit(SendUnit sendUnit) { - mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); - return true; + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; } StreamBase::SendMode StreamBase::sendMode() const { - return (StreamBase::SendMode) mControl->mode(); + return (StreamBase::SendMode) mControl->mode(); } bool StreamBase::setSendMode(SendMode sendMode) { - mControl->set_mode( - (OstProto::StreamControl::SendMode) sendMode); - return true; + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; } StreamBase::NextWhat StreamBase::nextWhat() const { - return (StreamBase::NextWhat) mControl->next(); + return (StreamBase::NextWhat) mControl->next(); } bool StreamBase::setNextWhat(NextWhat nextWhat) { - mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); - return true; + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; } quint32 StreamBase::numPackets() const { - return (quint32) mControl->num_packets(); + return (quint32) mControl->num_packets(); } bool StreamBase::setNumPackets(quint32 numPackets) { - mControl->set_num_packets(numPackets); - return true; + mControl->set_num_packets(numPackets); + return true; } quint32 StreamBase::numBursts() const { - return (quint32) mControl->num_bursts(); + return (quint32) mControl->num_bursts(); } bool StreamBase::setNumBursts(quint32 numBursts) { - mControl->set_num_bursts(numBursts); - return true; + mControl->set_num_bursts(numBursts); + return true; } quint32 StreamBase::burstSize() const { - return (quint32) mControl->packets_per_burst(); + return (quint32) mControl->packets_per_burst(); } bool StreamBase::setBurstSize(quint32 packetsPerBurst) { - mControl->set_packets_per_burst(packetsPerBurst); - return true; + mControl->set_packets_per_burst(packetsPerBurst); + return true; } quint32 StreamBase::packetRate() const { - return (quint32) mControl->packets_per_sec(); + return (quint32) mControl->packets_per_sec(); } bool StreamBase::setPacketRate(quint32 packetsPerSec) { - mControl->set_packets_per_sec(packetsPerSec); - return true; + mControl->set_packets_per_sec(packetsPerSec); + return true; } quint32 StreamBase::burstRate() const { - return (quint32) mControl->bursts_per_sec(); + return (quint32) mControl->bursts_per_sec(); } bool StreamBase::setBurstRate(quint32 burstsPerSec) { - mControl->set_bursts_per_sec(burstsPerSec); - return true; + mControl->set_bursts_per_sec(burstsPerSec); + return true; } bool StreamBase::isFrameVariable() const { - ProtocolListIterator *iter; + ProtocolListIterator *iter; - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol *proto; + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; - proto = iter->next(); - if (proto->isProtocolFrameValueVariable()) - goto _exit; - } - delete iter; - return false; + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; _exit: - delete iter; - return true; + delete iter; + return true; } int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const { - int pktLen, len = 0; + int pktLen, len = 0; - pktLen = frameLen(n); + pktLen = frameLen(n); - // pktLen is adjusted for CRC/FCS which will be added by the NIC - pktLen -= 4; + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= 4; - if ((pktLen < 0) || (pktLen > bufMaxSize)) - return 0; + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; - ProtocolListIterator *iter; + ProtocolListIterator *iter; - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol *proto; - QByteArray ba; + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; - proto = iter->next(); - ba = proto->protocolFrameValue(n); + proto = iter->next(); + ba = proto->protocolFrameValue(n); - if (len + ba.size() < bufMaxSize) - memcpy(buf+len, ba.constData(), ba.size()); - len += ba.size(); - } - delete iter; + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; - return pktLen; + return pktLen; } diff --git a/common/streambase.h b/common/streambase.h index 45f0cbb..dd30232 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -13,107 +13,107 @@ class ProtocolListIterator; class StreamBase { private: - OstProto::StreamId *mStreamId; - OstProto::StreamCore *mCore; - OstProto::StreamControl *mControl; + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; - ProtocolList *currentFrameProtocols; + ProtocolList *currentFrameProtocols; public: - StreamBase(); - ~StreamBase(); + StreamBase(); + ~StreamBase(); - void protoDataCopyFrom(const OstProto::Stream &stream); - void protoDataCopyInto(OstProto::Stream &stream) const; + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; - ProtocolListIterator* createProtocolListIterator() const; + ProtocolListIterator* createProtocolListIterator() const; - //! \todo (LOW) should we have a copy constructor?? + //! \todo (LOW) should we have a copy constructor?? public: - enum FrameLengthMode { - e_fl_fixed, - e_fl_inc, - e_fl_dec, - e_fl_random - }; + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; - enum SendUnit { - e_su_packets, - e_su_bursts - }; + enum SendUnit { + e_su_packets, + e_su_bursts + }; - enum SendMode { - e_sm_fixed, - e_sm_continuous - }; + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; - enum NextWhat { - e_nw_stop, - e_nw_goto_next, - e_nw_goto_id - }; + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; - bool operator < (const StreamBase &s) const; + bool operator < (const StreamBase &s) const; - quint32 id(); - bool setId(quint32 id); + quint32 id(); + bool setId(quint32 id); #if 0 // FIXME(HI): needed? - quint32 portId() - { return mCore->port_id();} - bool setPortId(quint32 id) - { mCore->set_port_id(id); return true;} + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} #endif - quint32 ordinal(); - bool setOrdinal(quint32 ordinal); + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); - bool isEnabled() const; - bool setEnabled(bool flag); + bool isEnabled() const; + bool setEnabled(bool flag); - const QString name() const ; - bool setName(QString name) ; + const QString name() const ; + bool setName(QString name) ; - // Frame Length (includes FCS); - FrameLengthMode lenMode() const; - bool setLenMode(FrameLengthMode lenMode); + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); - quint16 frameLen(int streamIndex = 0) const; - bool setFrameLen(quint16 frameLen); + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); - quint16 frameLenMin() const; - bool setFrameLenMin(quint16 frameLenMin); + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); - quint16 frameLenMax() const; - bool setFrameLenMax(quint16 frameLenMax); + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); - SendUnit sendUnit() const; - bool setSendUnit(SendUnit sendUnit); + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); - SendMode sendMode() const; - bool setSendMode(SendMode sendMode); + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); - NextWhat nextWhat() const; - bool setNextWhat(NextWhat nextWhat); + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); - quint32 numPackets() const; - bool setNumPackets(quint32 numPackets); + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); - quint32 numBursts() const; - bool setNumBursts(quint32 numBursts); + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); - quint32 burstSize() const; - bool setBurstSize(quint32 packetsPerBurst); + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); - quint32 packetRate() const; - bool setPacketRate(quint32 packetsPerSec); + quint32 packetRate() const; + bool setPacketRate(quint32 packetsPerSec); - quint32 burstRate() const; - bool setBurstRate(quint32 burstsPerSec); + quint32 burstRate() const; + bool setBurstRate(quint32 burstsPerSec); - bool isFrameVariable() const; - int frameValue(uchar *buf, int bufMaxSize, int n) const; + bool isFrameVariable() const; + int frameValue(uchar *buf, int bufMaxSize, int n) const; }; #endif diff --git a/common/svlan.cpp b/common/svlan.cpp index 55ef836..6d30cfc 100644 --- a/common/svlan.cpp +++ b/common/svlan.cpp @@ -4,10 +4,10 @@ #include "svlan.pb.h" SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) - : VlanProtocol(stream, parent) + : VlanProtocol(stream, parent) { - data.set_tpid(0x88a8); - data.set_is_override_tpid(true); + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); } SVlanProtocol::~SVlanProtocol() @@ -15,35 +15,35 @@ SVlanProtocol::~SVlanProtocol() } AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new SVlanProtocol(stream, parent); + return new SVlanProtocol(stream, parent); } quint32 SVlanProtocol::protocolNumber() const { - return OstProto::Protocol::kSvlanFieldNumber; + return OstProto::Protocol::kSvlanFieldNumber; } void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::svlan)) - data.MergeFrom(protocol.GetExtension(OstProto::svlan)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); } QString SVlanProtocol::name() const { - return QString("SVlan"); + return QString("SVlan"); } QString SVlanProtocol::shortName() const { - return QString("SVlan"); + return QString("SVlan"); } diff --git a/common/svlan.h b/common/svlan.h index 0fb4b97..3c7d673 100644 --- a/common/svlan.h +++ b/common/svlan.h @@ -6,18 +6,18 @@ class SVlanProtocol : public VlanProtocol { public: - SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~SVlanProtocol(); + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; }; #endif diff --git a/common/svlan.proto b/common/svlan.proto index 72e4557..42f45bc 100644 --- a/common/svlan.proto +++ b/common/svlan.proto @@ -4,5 +4,5 @@ import "vlan.proto"; package OstProto; extend Protocol { - optional Vlan svlan = 125; + optional Vlan svlan = 125; } diff --git a/common/tcp.cpp b/common/tcp.cpp index 6bbc271..b6990f5 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -4,458 +4,458 @@ #include "tcp.h" TcpConfigForm::TcpConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } TcpProtocol::~TcpProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new TcpProtocol(stream, parent); + return new TcpProtocol(stream, parent); } quint32 TcpProtocol::protocolNumber() const { - return OstProto::Protocol::kTcpFieldNumber; + return OstProto::Protocol::kTcpFieldNumber; } void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::tcp)) - data.MergeFrom(protocol.GetExtension(OstProto::tcp)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); } QString TcpProtocol::name() const { - return QString("Transmission Control Protocol"); + return QString("Transmission Control Protocol"); } QString TcpProtocol::shortName() const { - return QString("TCP"); + return QString("TCP"); } quint32 TcpProtocol::protocolId(ProtocolIdType type) const { - switch(type) - { - case ProtocolIdIp: return 0x06; - default: break; - } + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } - return AbstractProtocol::protocolId(type); + return AbstractProtocol::protocolId(type); } -int TcpProtocol::fieldCount() const +int TcpProtocol::fieldCount() const { - return tcp_fieldCount; + return tcp_fieldCount; } AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case tcp_src_port: - case tcp_dst_port: - case tcp_seq_num: - case tcp_ack_num: - case tcp_hdrlen: - case tcp_rsvd: - case tcp_flags: - case tcp_window: - break; + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; - case tcp_cksum: - flags |= FieldIsCksum; - break; + case tcp_cksum: + flags |= FieldIsCksum; + break; - case tcp_urg_ptr: - break; + case tcp_urg_ptr: + break; - case tcp_is_override_hdrlen: - case tcp_is_override_cksum: - flags |= FieldIsMeta; - break; + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags |= FieldIsMeta; + break; - default: - break; - } + default: + break; + } - return flags; + return flags; } QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case tcp_src_port: - switch(attrib) - { - case FieldName: - return QString("Source Port"); - case FieldValue: - return data.src_port(); - case FieldTextValue: - return QString("%1").arg(data.src_port()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + switch (index) + { + case tcp_src_port: + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return data.src_port(); + case FieldTextValue: + return QString("%1").arg(data.src_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case tcp_dst_port: - switch(attrib) - { - case FieldName: - return QString("Destination Port"); - case FieldValue: - return data.dst_port(); - case FieldTextValue: - return QString("%1").arg(data.dst_port()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case tcp_dst_port: + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return data.dst_port(); + case FieldTextValue: + return QString("%1").arg(data.dst_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case tcp_seq_num: - switch(attrib) - { - case FieldName: - return QString("Sequence Number"); - case FieldValue: - return data.seq_num(); - case FieldTextValue: - return QString("%1").arg(data.seq_num()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case tcp_ack_num: - switch(attrib) - { - case FieldName: - return QString("Acknowledgement Number"); - case FieldValue: - return data.ack_num(); - case FieldTextValue: - return QString("%1").arg(data.ack_num()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case tcp_hdrlen: - switch(attrib) - { - case FieldName: - return QString("Header Length"); - case FieldValue: - if (data.is_override_hdrlen()) - return ((data.hdrlen_rsvd() >> 4) & 0x0F); - else - return 5; - case FieldTextValue: - if (data.is_override_hdrlen()) - return QString("%1 bytes").arg( - 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); - else - return QString("20 bytes"); - case FieldFrameValue: - if (data.is_override_hdrlen()) - return QByteArray(1, - (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); - else - return QByteArray(1, (char) 0x05); - case FieldBitSize: - return 4; - default: - break; - } - break; + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; - case tcp_rsvd: - switch(attrib) - { - case FieldName: - return QString("Reserved"); - case FieldValue: - return (data.hdrlen_rsvd() & 0x0F); - case FieldTextValue: - return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); - case FieldFrameValue: - return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); - case FieldBitSize: - return 4; - default: - break; - } - break; + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; - case tcp_flags: - switch(attrib) - { - case FieldName: - return QString("Flags"); - case FieldValue: - return (data.flags()); - case FieldTextValue: - { - QString s; - s.append("URG: "); - s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); - s.append(" ACK: "); - s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); - s.append(" PSH: "); - s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); - s.append(" RST: "); - s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); - s.append(" SYN: "); - s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); - s.append(" FIN: "); - s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); - return s; - } - case FieldFrameValue: - return QByteArray(1, (char)(data.flags() & 0x3F)); - default: - break; - } - break; + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; - case tcp_window: - switch(attrib) - { - case FieldName: - return QString("Window Size"); - case FieldValue: - return data.window(); - case FieldTextValue: - return QString("%1").arg(data.window()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.window(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case tcp_cksum: - switch(attrib) - { - case FieldName: - return QString("Checksum"); - case FieldValue: - { - quint16 cksum; + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); - return cksum; - } - case FieldTextValue: - { - quint16 cksum; + return cksum; + } + case FieldTextValue: + { + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); - return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); - } - case FieldFrameValue: - { - quint16 cksum; + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); - QByteArray fv; - fv.resize(2); - qToBigEndian(cksum, (uchar*) fv.data()); - return fv; - } - case FieldBitSize: - return 16; - default: - break; - } - break; + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; - case tcp_urg_ptr: - switch(attrib) - { - case FieldName: - return QString("Urgent Pointer"); - case FieldValue: - return data.urg_ptr(); - case FieldTextValue: - return QString("%1").arg(data.urg_ptr()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - // Meta fields - case tcp_is_override_hdrlen: - case tcp_is_override_cksum: - default: - break; - } + // Meta fields + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool TcpProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } bool TcpProtocol::isProtocolFrameValueVariable() const { - if (data.is_override_cksum()) - return false; - else - return isProtocolFramePayloadValueVariable(); + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); } QWidget* TcpProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new TcpConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; } void TcpProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); - configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); + configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); + configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); - configForm->leTcpSeqNum->setText(QString().setNum(data.seq_num())); - configForm->leTcpAckNum->setText(QString().setNum(data.ack_num())); + configForm->leTcpSeqNum->setText(QString().setNum(data.seq_num())); + configForm->leTcpAckNum->setText(QString().setNum(data.ack_num())); - configForm->leTcpHdrLen->setText(fieldData(tcp_hdrlen, FieldValue).toString()); - configForm->cbTcpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leTcpHdrLen->setText(fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked(data.is_override_hdrlen()); - configForm->leTcpWindow->setText(QString().setNum(data.window())); + configForm->leTcpWindow->setText(QString().setNum(data.window())); - configForm->leTcpCksum->setText(QString("%1").arg( - fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbTcpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked(data.is_override_cksum()); - configForm->leTcpUrgentPointer->setText(QString().setNum(data.urg_ptr())); + configForm->leTcpUrgentPointer->setText(QString().setNum(data.urg_ptr())); - configForm->cbTcpFlagsUrg->setChecked((data.flags() & TCP_FLAG_URG) > 0); - configForm->cbTcpFlagsAck->setChecked((data.flags() & TCP_FLAG_ACK) > 0); - configForm->cbTcpFlagsPsh->setChecked((data.flags() & TCP_FLAG_PSH) > 0); - configForm->cbTcpFlagsRst->setChecked((data.flags() & TCP_FLAG_RST) > 0); - configForm->cbTcpFlagsSyn->setChecked((data.flags() & TCP_FLAG_SYN) > 0); - configForm->cbTcpFlagsFin->setChecked((data.flags() & TCP_FLAG_FIN) > 0); + configForm->cbTcpFlagsUrg->setChecked((data.flags() & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((data.flags() & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((data.flags() & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((data.flags() & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((data.flags() & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((data.flags() & TCP_FLAG_FIN) > 0); } void TcpProtocol::storeConfigWidget() { - bool isOk; - int ff = 0; + bool isOk; + int ff = 0; - configWidget(); + configWidget(); - data.set_src_port(configForm->leTcpSrcPort->text().toULong(&isOk)); - data.set_dst_port(configForm->leTcpDstPort->text().toULong(&isOk)); + data.set_src_port(configForm->leTcpSrcPort->text().toULong(&isOk)); + data.set_dst_port(configForm->leTcpDstPort->text().toULong(&isOk)); - data.set_seq_num(configForm->leTcpSeqNum->text().toULong(&isOk)); - data.set_ack_num(configForm->leTcpAckNum->text().toULong(&isOk)); + data.set_seq_num(configForm->leTcpSeqNum->text().toULong(&isOk)); + data.set_ack_num(configForm->leTcpAckNum->text().toULong(&isOk)); - data.set_hdrlen_rsvd((configForm->leTcpHdrLen->text().toULong(&isOk) << 4) & 0xF0); - data.set_is_override_hdrlen(configForm->cbTcpHdrLenOverride->isChecked()); + data.set_hdrlen_rsvd((configForm->leTcpHdrLen->text().toULong(&isOk) << 4) & 0xF0); + data.set_is_override_hdrlen(configForm->cbTcpHdrLenOverride->isChecked()); - data.set_window(configForm->leTcpWindow->text().toULong(&isOk)); + data.set_window(configForm->leTcpWindow->text().toULong(&isOk)); - data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_is_override_cksum(configForm->cbTcpCksumOverride->isChecked()); + data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_is_override_cksum(configForm->cbTcpCksumOverride->isChecked()); - data.set_urg_ptr(configForm->leTcpUrgentPointer->text().toULong(&isOk)); + data.set_urg_ptr(configForm->leTcpUrgentPointer->text().toULong(&isOk)); - if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; - if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; - if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; - if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; - if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; - if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; - data.set_flags(ff); + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + data.set_flags(ff); } diff --git a/common/tcp.h b/common/tcp.h index 72cfbf8..8ef7ebf 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -6,72 +6,72 @@ #include "tcp.pb.h" #include "ui_tcp.h" -#define TCP_FLAG_URG 0x20 -#define TCP_FLAG_ACK 0x10 -#define TCP_FLAG_PSH 0x08 -#define TCP_FLAG_RST 0x04 -#define TCP_FLAG_SYN 0x02 -#define TCP_FLAG_FIN 0x01 +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 class TcpConfigForm : public QWidget, public Ui::tcp { - Q_OBJECT + Q_OBJECT public: - TcpConfigForm(QWidget *parent = 0); + TcpConfigForm(QWidget *parent = 0); }; class TcpProtocol : public AbstractProtocol { private: - OstProto::Tcp data; - TcpConfigForm *configForm; - enum tcpfield - { - tcp_src_port = 0, - tcp_dst_port, - tcp_seq_num, - tcp_ack_num, - tcp_hdrlen, - tcp_rsvd, - tcp_flags, - tcp_window, - tcp_cksum, - tcp_urg_ptr, + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, - tcp_is_override_hdrlen, - tcp_is_override_cksum, + tcp_is_override_hdrlen, + tcp_is_override_cksum, - tcp_fieldCount - }; + tcp_fieldCount + }; public: - TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~TcpProtocol(); + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; - virtual quint32 protocolId(ProtocolIdType type) const; + virtual QString name() const; + virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameValueVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/tcp.proto b/common/tcp.proto index 3cc6135..05dd181 100644 --- a/common/tcp.proto +++ b/common/tcp.proto @@ -4,24 +4,24 @@ package OstProto; // Tcp message Tcp { - optional bool is_override_hdrlen = 1; - optional bool is_override_cksum = 2; + optional bool is_override_hdrlen = 1; + optional bool is_override_cksum = 2; - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; - optional uint32 seq_num = 5 [default = 129018]; - optional uint32 ack_num = 6; + optional uint32 seq_num = 5 [default = 129018]; + optional uint32 ack_num = 6; - optional uint32 hdrlen_rsvd = 7 [default = 0x50]; - optional uint32 flags = 8; + optional uint32 hdrlen_rsvd = 7 [default = 0x50]; + optional uint32 flags = 8; - optional uint32 window = 9 [default = 1024]; - optional uint32 cksum = 10; - optional uint32 urg_ptr = 11; + optional uint32 window = 9 [default = 1024]; + optional uint32 cksum = 10; + optional uint32 urg_ptr = 11; } extend Protocol { - optional Tcp tcp = 140; + optional Tcp tcp = 140; } diff --git a/common/udp.cpp b/common/udp.cpp index 9cb74ec..e65e0af 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -4,306 +4,306 @@ #include "udp.h" UdpConfigForm::UdpConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } UdpProtocol::~UdpProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new UdpProtocol(stream, parent); + return new UdpProtocol(stream, parent); } quint32 UdpProtocol::protocolNumber() const { - return OstProto::Protocol::kUdpFieldNumber; + return OstProto::Protocol::kUdpFieldNumber; } void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::udp)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::udp)) - data.MergeFrom(protocol.GetExtension(OstProto::udp)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); } QString UdpProtocol::name() const { - return QString("User Datagram Protocol"); + return QString("User Datagram Protocol"); } QString UdpProtocol::shortName() const { - return QString("UDP"); + return QString("UDP"); } quint32 UdpProtocol::protocolId(ProtocolIdType type) const { - switch(type) - { - case ProtocolIdIp: return 0x11; - default: break; - } + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } - return AbstractProtocol::protocolId(type); + return AbstractProtocol::protocolId(type); } -int UdpProtocol::fieldCount() const +int UdpProtocol::fieldCount() const { - return udp_fieldCount; + return udp_fieldCount; } AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case udp_srcPort: - case udp_dstPort: - case udp_totLen: - break; + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; - case udp_cksum: - flags |= FieldIsCksum; - break; + case udp_cksum: + flags |= FieldIsCksum; + break; - case udp_isOverrideTotLen: - case udp_isOverrideCksum: - flags |= FieldIsMeta; - break; + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags |= FieldIsMeta; + break; - default: - break; - } + default: + break; + } - return flags; + return flags; } QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case udp_srcPort: - switch(attrib) - { - case FieldName: - return QString("Source Port"); - case FieldValue: - return data.src_port(); - case FieldTextValue: - return QString("%1").arg(data.src_port()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + switch (index) + { + case udp_srcPort: + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return data.src_port(); + case FieldTextValue: + return QString("%1").arg(data.src_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case udp_dstPort: - switch(attrib) - { - case FieldName: - return QString("Destination Port"); - case FieldValue: - return data.dst_port(); - case FieldTextValue: - return QString("%1").arg(data.dst_port()); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; + case udp_dstPort: + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return data.dst_port(); + case FieldTextValue: + return QString("%1").arg(data.dst_port()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; - case udp_totLen: - { + case udp_totLen: + { - switch(attrib) - { - case FieldName: - return QString("Datagram Length"); - case FieldValue: - { - int totlen; + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; - totlen = data.is_override_totlen() ? - data.totlen() : - (protocolFramePayloadSize(streamIndex) + 8); - return totlen; - } - case FieldFrameValue: - { - QByteArray fv; - int totlen; - totlen = data.is_override_totlen() ? - data.totlen() : - (protocolFramePayloadSize(streamIndex) + 8); - fv.resize(2); - qToBigEndian((quint16) totlen, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - { - int totlen; - totlen = data.is_override_totlen() ? - data.totlen() : - (protocolFramePayloadSize(streamIndex) + 8); - return QString("%1").arg(totlen); - } - case FieldBitSize: - return 16; - default: - break; - } - break; - } - case udp_cksum: - { - quint16 cksum; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; - switch(attrib) - { - case FieldValue: - case FieldFrameValue: - case FieldTextValue: - { - if (data.is_override_cksum()) - cksum = data.cksum(); - else - cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); - qDebug("UDP cksum = %hu", cksum); - } - default: - break; - } + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + } + default: + break; + } - switch(attrib) - { - case FieldName: - return QString("Checksum"); - case FieldValue: - return cksum; - case FieldFrameValue: - { - QByteArray fv; + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; - fv.resize(2); - qToBigEndian(cksum, (uchar*) fv.data()); - return fv; - } - case FieldTextValue: - return QString("0x%1"). - arg(cksum, 4, BASE_HEX, QChar('0'));; - case FieldBitSize: - return 16; - default: - break; - } - break; - } - // Meta fields - case udp_isOverrideTotLen: - case udp_isOverrideCksum: - switch(attrib) - { - case FieldIsMeta: - return true; - default: - break; - } - break; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + // Meta fields + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + switch(attrib) + { + case FieldIsMeta: + return true; + default: + break; + } + break; - default: - break; - } + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool UdpProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - //! implement UdpProtocol::setFieldData() - return false; + //! implement UdpProtocol::setFieldData() + return false; } bool UdpProtocol::isProtocolFrameValueVariable() const { - if (data.is_override_totlen() && data.is_override_cksum()) - return false; - else - return isProtocolFramePayloadValueVariable(); + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); } QWidget* UdpProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new UdpConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; } void UdpProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->leUdpSrcPort->setText(fieldData(udp_srcPort, FieldValue).toString()); - configForm->leUdpDstPort->setText(fieldData(udp_dstPort, FieldValue).toString()); + configForm->leUdpSrcPort->setText(fieldData(udp_srcPort, FieldValue).toString()); + configForm->leUdpDstPort->setText(fieldData(udp_dstPort, FieldValue).toString()); - configForm->leUdpLength->setText(fieldData(udp_totLen, FieldValue).toString()); - configForm->cbUdpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leUdpLength->setText(fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked(data.is_override_totlen()); - configForm->leUdpCksum->setText(QString("%1").arg( - fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbUdpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked(data.is_override_cksum()); } void UdpProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_src_port(configForm->leUdpSrcPort->text().toULong(&isOk)); - data.set_dst_port(configForm->leUdpDstPort->text().toULong(&isOk)); + data.set_src_port(configForm->leUdpSrcPort->text().toULong(&isOk)); + data.set_dst_port(configForm->leUdpDstPort->text().toULong(&isOk)); - data.set_totlen(configForm->leUdpLength->text().toULong(&isOk)); - data.set_is_override_totlen(configForm->cbUdpLengthOverride->isChecked()); + data.set_totlen(configForm->leUdpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbUdpLengthOverride->isChecked()); - data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_is_override_cksum(configForm->cbUdpCksumOverride->isChecked()); + data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_is_override_cksum(configForm->cbUdpCksumOverride->isChecked()); } diff --git a/common/udp.h b/common/udp.h index 663456b..652c03a 100644 --- a/common/udp.h +++ b/common/udp.h @@ -8,57 +8,57 @@ class UdpConfigForm : public QWidget, public Ui::udp { - Q_OBJECT + Q_OBJECT public: - UdpConfigForm(QWidget *parent = 0); + UdpConfigForm(QWidget *parent = 0); }; class UdpProtocol : public AbstractProtocol { private: - OstProto::Udp data; - UdpConfigForm *configForm; - enum udpfield - { - udp_srcPort = 0, - udp_dstPort, - udp_totLen, - udp_cksum, + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, - udp_isOverrideTotLen, - udp_isOverrideCksum, + udp_isOverrideTotLen, + udp_isOverrideCksum, - udp_fieldCount - }; + udp_fieldCount + }; public: - UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~UdpProtocol(); + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; - virtual quint32 protocolId(ProtocolIdType type) const; + virtual QString name() const; + virtual QString shortName() const; + virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameValueVariable() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/udp.proto b/common/udp.proto index 89e3065..23ed43e 100644 --- a/common/udp.proto +++ b/common/udp.proto @@ -4,15 +4,15 @@ package OstProto; // UDP message Udp { - optional bool is_override_totlen = 1; - optional bool is_override_cksum = 2; + optional bool is_override_totlen = 1; + optional bool is_override_cksum = 2; - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; - optional uint32 totlen = 5; - optional uint32 cksum = 6; + optional uint32 src_port = 3 [default = 8902]; + optional uint32 dst_port = 4 [default = 80]; + optional uint32 totlen = 5; + optional uint32 cksum = 6; } extend Protocol { - optional Udp udp = 141; + optional Udp udp = 141; } diff --git a/common/userscript.proto b/common/userscript.proto index 3f26cd2..aaf2621 100644 --- a/common/userscript.proto +++ b/common/userscript.proto @@ -5,10 +5,10 @@ package OstProto; // Sample Protocol message UserScript { - optional string program = 1; + optional string program = 1; } extend Protocol { - optional UserScript userScript = 54; + optional UserScript userScript = 54; } diff --git a/common/vlan.cpp b/common/vlan.cpp index 244a473..83ed52d 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -3,235 +3,235 @@ #include "vlan.h" VlanConfigForm::VlanConfigForm(QWidget *parent) - : QWidget(parent) + : QWidget(parent) { - setupUi(this); + setupUi(this); } VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) - : AbstractProtocol(stream, parent) + : AbstractProtocol(stream, parent) { - configForm = NULL; + configForm = NULL; } VlanProtocol::~VlanProtocol() { - delete configForm; + delete configForm; } AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) + AbstractProtocol *parent) { - return new VlanProtocol(stream, parent); + return new VlanProtocol(stream, parent); } quint32 VlanProtocol::protocolNumber() const { - return OstProto::Protocol::kVlanFieldNumber; + return OstProto::Protocol::kVlanFieldNumber; } void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { - protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); - protocol.mutable_protocol_id()->set_id(protocolNumber()); + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); } void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { - if (protocol.protocol_id().id() == protocolNumber() && - protocol.HasExtension(OstProto::vlan)) - data.MergeFrom(protocol.GetExtension(OstProto::vlan)); + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); } QString VlanProtocol::name() const { - return QString("Vlan"); + return QString("Vlan"); } QString VlanProtocol::shortName() const { - return QString("Vlan"); + return QString("Vlan"); } -int VlanProtocol::fieldCount() const +int VlanProtocol::fieldCount() const { - return vlan_fieldCount; + return vlan_fieldCount; } AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const { - AbstractProtocol::FieldFlags flags; + AbstractProtocol::FieldFlags flags; - flags = AbstractProtocol::fieldFlags(index); + flags = AbstractProtocol::fieldFlags(index); - switch (index) - { - case vlan_tpid: - case vlan_prio: - case vlan_cfiDei: - case vlan_vlanId: - break; + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; - // meta-fields - case vlan_isOverrideTpid: - flags |= FieldIsMeta; - break; - } + // meta-fields + case vlan_isOverrideTpid: + flags |= FieldIsMeta; + break; + } - return flags; + return flags; } QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, - int streamIndex) const + int streamIndex) const { - switch (index) - { - case vlan_tpid: - { - quint16 tpid; + switch (index) + { + case vlan_tpid: + { + quint16 tpid; - tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; - switch(attrib) - { - case FieldName: - return QString("Tag Protocol Id"); - case FieldValue: - return tpid; - case FieldTextValue: - return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian(tpid, (uchar*) fv.data()); - return fv; - } - default: - break; - } - break; - } + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } - case vlan_prio: - { - uint prio = ((data.vlan_tag() >> 13) & 0x07); + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); - switch(attrib) - { - case FieldName: - return QString("Priority"); - case FieldValue: - return prio; - case FieldTextValue: - return QString("%1").arg(prio); - case FieldFrameValue: - return QByteArray(1, (char) prio); - case FieldBitSize: - return 3; - default: - break; - } - break; - } + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } - case vlan_cfiDei: - { - uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); - switch(attrib) - { - case FieldName: - return QString("CFI/DEI"); - case FieldValue: - return cfiDei; - case FieldTextValue: - return QString("%1").arg(cfiDei); - case FieldFrameValue: - return QByteArray(1, (char) cfiDei); - case FieldBitSize: - return 1; - default: - break; - } - break; - } + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } - case vlan_vlanId: - { - quint16 vlanId = (data.vlan_tag() & 0x0FFF); + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); - switch(attrib) - { - case FieldName: - return QString("VLAN Id"); - case FieldValue: - return vlanId; - case FieldTextValue: - return QString("%1").arg(vlanId); - case FieldFrameValue: - { - QByteArray fv; - fv.resize(2); - qToBigEndian((quint16) vlanId, (uchar*) fv.data()); - return fv; - } - case FieldBitSize: - return 12; - default: - break; - } - break; - } - // Meta fields + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields - case vlan_isOverrideTpid: - default: - break; - } + case vlan_isOverrideTpid: + default: + break; + } - return AbstractProtocol::fieldData(index, attrib, streamIndex); + return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool VlanProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) + FieldAttrib attrib) { - return false; + return false; } QWidget* VlanProtocol::configWidget() { - if (configForm == NULL) - { - configForm = new VlanConfigForm; - loadConfigWidget(); - } - return configForm; + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; } void VlanProtocol::loadConfigWidget() { - configWidget(); + configWidget(); - configForm->cbTpidOverride->setChecked(data.is_override_tpid()); - configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); - configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); - configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); - configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); } void VlanProtocol::storeConfigWidget() { - bool isOk; + bool isOk; - configWidget(); + configWidget(); - data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); - data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_vlan_tag( - ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | - ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | - (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); } diff --git a/common/vlan.h b/common/vlan.h index 5e5d7de..f276ce5 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -8,56 +8,56 @@ class VlanConfigForm : public QWidget, public Ui::Vlan { - Q_OBJECT + Q_OBJECT public: - VlanConfigForm(QWidget *parent = 0); + VlanConfigForm(QWidget *parent = 0); }; class VlanProtocol : public AbstractProtocol { private: - VlanConfigForm *configForm; - enum Vlanfield - { - vlan_tpid, - vlan_prio, - vlan_cfiDei, - vlan_vlanId, + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, - // meta-fields - vlan_isOverrideTpid, + // meta-fields + vlan_isOverrideTpid, - vlan_fieldCount - }; + vlan_fieldCount + }; protected: - OstProto::Vlan data; + OstProto::Vlan data; public: - VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~VlanProtocol(); + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); - static AbstractProtocol* createInstance(StreamBase *stream, - AbstractProtocol *parent = 0); - virtual quint32 protocolNumber() const; + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; - virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; - virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); - virtual QString name() const; - virtual QString shortName() const; + virtual QString name() const; + virtual QString shortName() const; - virtual int fieldCount() const; + virtual int fieldCount() const; - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); }; #endif diff --git a/common/vlan.proto b/common/vlan.proto index 0232612..a963444 100644 --- a/common/vlan.proto +++ b/common/vlan.proto @@ -2,14 +2,14 @@ import "protocol.proto"; package OstProto; message Vlan { - // VLAN presence/absence - optional bool is_override_tpid = 1; + // VLAN presence/absence + optional bool is_override_tpid = 1; - // VLAN values - optional uint32 tpid = 2; - optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid } extend Protocol { - optional Vlan vlan = 126; + optional Vlan vlan = 126; } diff --git a/common/vlanstack.h b/common/vlanstack.h index f74aaed..4b24464 100644 --- a/common/vlanstack.h +++ b/common/vlanstack.h @@ -6,6 +6,6 @@ #include "vlan.h" typedef ComboProtocol VlanStackProtocol; + SVlanProtocol, VlanProtocol> VlanStackProtocol; #endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto index a54f11f..4e6e9a0 100644 --- a/common/vlanstack.proto +++ b/common/vlanstack.proto @@ -4,9 +4,9 @@ package OstProto; // Stacked VLAN (2 tags) message VlanStack { - // Empty since this is a 'combo' protocol + // Empty since this is a 'combo' protocol } extend Protocol { - optional VlanStack vlanStack = 129; + optional VlanStack vlanStack = 129; } diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index 7a70c75..ba2b5fe 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -11,141 +11,141 @@ class PbHelper { public: - // FIXME: Change msg from * to & - void ForceSetSingularDefault(::google::protobuf::Message *msg) - { - const ::google::protobuf::Descriptor *desc; - ::google::protobuf::Message::Reflection *refl; + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - desc = msg->GetDescriptor(); - refl = msg->GetReflection(); + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); - for (int i=0; i < desc->field_count(); i++) - { - const ::google::protobuf::FieldDescriptor *f; + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; - f = desc->field(i); + f = desc->field(i); - // Ensure field is singular and not already set - if (f->label() == - ::google::protobuf::FieldDescriptor::LABEL_REPEATED) - continue; - if (refl->HasField(f)) - continue; + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; - switch(f->type()) - { - case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: - refl->SetDouble(f, refl->GetDouble(f)); - break; + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: - refl->SetFloat(f, refl->GetFloat(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_INT32: - case ::google::protobuf::FieldDescriptor::TYPE_SINT32: - case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: - refl->SetInt32(f, refl->GetInt32(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_INT64: - case ::google::protobuf::FieldDescriptor::TYPE_SINT64: - case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: - refl->SetInt64(f, refl->GetInt64(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_UINT32: - case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: - refl->SetUInt32(f, refl->GetUInt32(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_UINT64: - case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: - refl->SetUInt64(f, refl->GetUInt64(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_BOOL: - refl->SetBool(f, refl->GetBool(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_ENUM: - refl->SetEnum(f, refl->GetEnum(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_STRING: - case ::google::protobuf::FieldDescriptor::TYPE_BYTES: - refl->SetString(f, refl->GetString(f)); - break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; - case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: - case ::google::protobuf::FieldDescriptor::TYPE_GROUP: - ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! - break; + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; - default: - qDebug("unhandled Field Type"); - break; - } - } - } + default: + qDebug("unhandled Field Type"); + break; + } + } + } - bool update( - ::google::protobuf::Message *target, - ::google::protobuf::Message *source) - { - // FIXME(HI): Depracate: use MergeFrom() directly - qDebug("In %s", __FUNCTION__); - target->MergeFrom(*source); - return true; + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; #if 0 - ::google::protobuf::Message::Reflection *sourceRef; - ::google::protobuf::Message::Reflection *targetRef; - std::vector srcFieldList; + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; - if (source->GetDescriptor()->full_name() != - target->GetDescriptor()->full_name()) - goto _error_exit; + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; - sourceRef = source->GetReflection(); - targetRef = target->GetReflection(); + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); - sourceRef->ListFields(&srcFieldList); - for (uint i=0; i < srcFieldList.size(); i++) - { - const ::google::protobuf::FieldDescriptor *srcField, *targetField; + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; - srcField = srcFieldList[i]; - targetField = target->GetDescriptor()->FindFieldByName( - srcField->name()); + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); - switch(targetField->type()) - { - case ::google::protobuf::FieldDescriptor::TYPE_UINT32: - targetRef->SetUInt32(targetField, - sourceRef->GetUInt32(srcField)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_BOOL: - targetRef->SetBool(targetField, - sourceRef->GetBool(srcField)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_STRING: - targetRef->SetString(targetField, - sourceRef->GetString(srcField)); - break; - default: - qDebug("unhandled Field Type"); - break; - } - } - _error_exit: - qDebug("%s: error!", __FUNCTION__); - return false; + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; #endif - } + } }; #endif #endif diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index f99c00b..5c3626e 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -2,309 +2,309 @@ PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) { - isPending = false; - pendingMethodId = -1; // don't care as long as isPending is false + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false - controller = NULL; - done = NULL; - response = NULL; + controller = NULL; + done = NULL; + response = NULL; - mServerAddress = ip; - mServerPort = port; - mpSocket = new QTcpSocket(this); + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); - // FIXME: Not quite sure why this ain't working! - // QMetaObject::connectSlotsByName(this); + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); - connect(mpSocket, SIGNAL(connected()), - this, SLOT(on_mpSocket_connected())); - connect(mpSocket, SIGNAL(disconnected()), - this, SLOT(on_mpSocket_disconnected())); - connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); - connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); - connect(mpSocket, SIGNAL(readyRead()), - this, SLOT(on_mpSocket_readyRead())); + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); } PbRpcChannel::~PbRpcChannel() { - delete mpSocket; + delete mpSocket; } void PbRpcChannel::establish() { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - mpSocket->connectToHost(mServerAddress, mServerPort); + mpSocket->connectToHost(mServerAddress, mServerPort); } void PbRpcChannel::establish(QHostAddress ip, quint16 port) { - mServerAddress = ip; - mServerPort = port; - establish(); + mServerAddress = ip; + mServerPort = port; + establish(); } void PbRpcChannel::tearDown() { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - mpSocket->disconnectFromHost(); + mpSocket->disconnectFromHost(); } void PbRpcChannel::CallMethod( - const ::google::protobuf::MethodDescriptor *method, - ::google::protobuf::RpcController *controller, - const ::google::protobuf::Message *req, - ::google::protobuf::Message *response, - ::google::protobuf::Closure* done) + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) { - char msg[MSGBUF_SIZE]; - int len; - bool ret; + char msg[MSGBUF_SIZE]; + int len; + bool ret; - if (isPending) - { - RpcCall call; - qDebug("RpcChannel: queueing method %d since %d is pending", - method->index(), pendingMethodId); + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending", + method->index(), pendingMethodId); - call.method = method; - call.controller = controller; - call.request = req; - call.response = response; - call.done = done; + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; - pendingCallList.append(call); + pendingCallList.append(call); - Q_ASSERT(pendingCallList.size() < 100); + Q_ASSERT(pendingCallList.size() < 100); - return; - } + return; + } - if (!req->IsInitialized()) - { - qWarning("RpcChannel: missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); - qFatal("exiting"); + qFatal("exiting"); - controller->SetFailed("Required fields missing"); - done->Run(); - return; - } + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } - pendingMethodId = method->index(); - this->controller=controller; - this->done=done; - this->response=response; - isPending = true; + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; - ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); - Q_ASSERT(ret == true); + ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); + Q_ASSERT(ret == true); - len = req->ByteSize(); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type - *((quint16*)(&msg[2])) = HTONS(method->index()); // method id - *((quint32*)(&msg[4])) = HTONL(len); // len + len = req->ByteSize(); + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type + *((quint16*)(&msg[2])) = HTONS(method->index()); // method id + *((quint32*)(&msg[4])) = HTONL(len); // len - // Avoid printing stats since it happens every couple of seconds - if (pendingMethodId != 12) - { - qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, - PB_HDR_SIZE + len, req->DebugString().c_str()); - BUFDUMP(msg, PB_HDR_SIZE + len); - } + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 12) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE + len); + } - mpSocket->write(msg, PB_HDR_SIZE + len); + mpSocket->write(msg, PB_HDR_SIZE + len); } void PbRpcChannel::on_mpSocket_readyRead() { - char msg[MSGBUF_SIZE]; - char *p = (char*)&msg; - int msgLen; - static bool parsing = false; - static quint16 type, method; - static quint32 len; + char msg[MSGBUF_SIZE]; + char *p = (char*)&msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; - //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); - if (!parsing) - { - // Do we have an entire header? If not, we'll wait ... - if (mpSocket->bytesAvailable() < PB_HDR_SIZE) - { - qDebug("client: not enough data available for a complete header"); - return; - } + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } - msgLen = mpSocket->read(msg, PB_HDR_SIZE); + msgLen = mpSocket->read(msg, PB_HDR_SIZE); - Q_ASSERT(msgLen == PB_HDR_SIZE); + Q_ASSERT(msgLen == PB_HDR_SIZE); - type = NTOHS(GET16(p+0)); - method = NTOHS(GET16(p+2)); - len = NTOHL(GET32(p+4)); + type = NTOHS(GET16(p+0)); + method = NTOHS(GET16(p+2)); + len = NTOHL(GET32(p+4)); - //BUFDUMP(msg, PB_HDR_SIZE); - //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); - parsing = true; - } + parsing = true; + } - switch (type) - { - case PB_MSG_TYPE_BINBLOB: - { - static quint32 cumLen = 0; - QIODevice *blob; + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; - blob = static_cast(controller)->binaryBlob(); - Q_ASSERT(blob != NULL); + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); - while ((cumLen < len) && mpSocket->bytesAvailable()) - { - int l; + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; - l = mpSocket->read(msg, sizeof(msg)); - blob->write(msg, l); - cumLen += l; - } + l = mpSocket->read(msg, sizeof(msg)); + blob->write(msg, l); + cumLen += l; + } - qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); - if (cumLen < len) - return; + if (cumLen < len) + return; - cumLen = 0; + cumLen = 0; - if (!isPending) - { - qDebug("not waiting for response"); - goto _error_exit; - } + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } - if (pendingMethodId != method) - { - qDebug("invalid method id %d (expected = %d)", method, - pendingMethodId); - goto _error_exit; - } + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } - break; - } + break; + } - case PB_MSG_TYPE_RESPONSE: - // Wait till we have the entire message - if (mpSocket->bytesAvailable() < len) - { - qDebug("client: not enough data available for a complete msg"); - return; - } - - msgLen = mpSocket->read(msg, sizeof(msg)); + case PB_MSG_TYPE_RESPONSE: + // Wait till we have the entire message + if (mpSocket->bytesAvailable() < len) + { + qDebug("client: not enough data available for a complete msg"); + return; + } + + msgLen = mpSocket->read(msg, sizeof(msg)); - Q_ASSERT((unsigned) msgLen == len); + Q_ASSERT((unsigned) msgLen == len); - //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); - //BUFDUMP(msg, msgLen); + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); - if (!isPending) - { - qDebug("not waiting for response"); - goto _error_exit; - } + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } - if (pendingMethodId != method) - { - qDebug("invalid method id %d (expected = %d)", method, - pendingMethodId); - goto _error_exit; - } + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } - response->ParseFromArray((void*) msg, len); + response->ParseFromArray((void*) msg, len); - // Avoid printing stats - if (method != 12) - { - qDebug("client(%s): Parsed as %s", __FUNCTION__, - response->DebugString().c_str()); - } + // Avoid printing stats + if (method != 12) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } - if (!response->IsInitialized()) - { - qWarning("RpcChannel: missing required fields in response"); - qDebug(response->InitializationErrorString().c_str()); + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug(response->InitializationErrorString().c_str()); - controller->SetFailed("Required fields missing"); - } - break; + controller->SetFailed("Required fields missing"); + } + break; - default: - qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); - goto _error_exit; - - } + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } - pendingMethodId = -1; - controller = NULL; - response = NULL; - isPending = false; - parsing = false; + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; - done->Run(); + done->Run(); - if (pendingCallList.size()) - { - RpcCall call = pendingCallList.takeFirst(); - CallMethod(call.method, call.controller, call.request, call.response, - call.done); - } + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } - return; + return; _error_exit: - parsing = false; - qDebug("client(%s) discarding received msg", __FUNCTION__); - return; + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; } void PbRpcChannel::on_mpSocket_stateChanged( - QAbstractSocket::SocketState socketState) + QAbstractSocket::SocketState socketState) { - qDebug("In %s", __FUNCTION__); - emit stateChanged(socketState); + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); } void PbRpcChannel::on_mpSocket_connected() { - qDebug("In %s", __FUNCTION__); - emit connected(); + qDebug("In %s", __FUNCTION__); + emit connected(); } void PbRpcChannel::on_mpSocket_disconnected() { - qDebug("In %s", __FUNCTION__); + qDebug("In %s", __FUNCTION__); - pendingMethodId = -1; - controller = NULL; - response = NULL; - isPending = false; - // \todo convert parsing from static to data member - //parsing = false - pendingCallList.clear(); + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); - emit disconnected(); + emit disconnected(); } void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) { - qDebug("In %s", __FUNCTION__); - emit error(socketError); + qDebug("In %s", __FUNCTION__); + emit error(socketError); } diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h index 17f287a..e467781 100644 --- a/rpc/pbrpcchannel.h +++ b/rpc/pbrpcchannel.h @@ -13,71 +13,71 @@ class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel { - Q_OBJECT - - // If isPending is TRUE, then controller, done, response - // and pendingMethodId correspond to the last method called by - // the service stub - bool isPending; - int pendingMethodId; + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; - // controller, done, response are set to the corresponding values - // passed by the stub to CallMethod(). They are reset to NULL when - // we get a response back from the server in on_mpSocket_readyRead() - // after calling done->Run(). + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). - /*! \todo (MED) : change controller, done and response to references - instead of pointers? */ - ::google::protobuf::RpcController *controller; - ::google::protobuf::Closure *done; - ::google::protobuf::Message *response; + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; - typedef struct _RpcCall { - const ::google::protobuf::MethodDescriptor *method; - ::google::protobuf::RpcController *controller; - const ::google::protobuf::Message *request; - ::google::protobuf::Message *response; - ::google::protobuf::Closure *done; - } RpcCall; - QList pendingCallList; + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; - QHostAddress mServerAddress; - quint16 mServerPort; - QTcpSocket *mpSocket; + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; public: - PbRpcChannel(QHostAddress ip, quint16 port); - ~PbRpcChannel(); + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); - void establish(); - void establish(QHostAddress ip, quint16 port); - void tearDown(); + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); - const QHostAddress& serverAddress() const { return mServerAddress; } - quint16 serverPort() const { return mServerPort; } + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } - QAbstractSocket::SocketState state() const - { return mpSocket->state(); } + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } - void CallMethod(const ::google::protobuf::MethodDescriptor *method, - ::google::protobuf::RpcController *controller, - const ::google::protobuf::Message *req, - ::google::protobuf::Message *response, - ::google::protobuf::Closure* done); + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); signals: - void connected(); - void disconnected(); - void error(QAbstractSocket::SocketError socketError); - void stateChanged(QAbstractSocket::SocketState socketState); + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); private slots: - void on_mpSocket_connected(); - void on_mpSocket_disconnected(); - void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); - void on_mpSocket_error(QAbstractSocket::SocketError socketError); + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); - void on_mpSocket_readyRead(); + void on_mpSocket_readyRead(); }; #endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index ef4b1ee..4ea1281 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -3,18 +3,18 @@ //! \todo (LOW) check which one is right - wrong one seems to be working!!!!! #if 0 -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+0) << 8 ) \ - | (*((quint8*)(p)+1))) +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+0) << 8 ) \ + | (*((quint8*)(p)+1))) #else -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) -#define GET32(p) (quint32)( \ - (*((quint8*)(p)+3) << 24) \ - | (*((quint8*)(p)+2) << 16) \ - | (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) +#define GET32(p) (quint32)( \ + (*((quint8*)(p)+3) << 24) \ + | (*((quint8*)(p)+2) << 16) \ + | (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) #endif #define BYTESWAP4(x) \ @@ -29,33 +29,33 @@ //! \todo (LOW) : portability #if 1 -#define HTONL(x) BYTESWAP4(x) -#define NTOHL(x) BYTESWAP4(x) -#define HTONS(x) BYTESWAP2(x) -#define NTOHS(x) BYTESWAP2(x) +#define HTONL(x) BYTESWAP4(x) +#define NTOHL(x) BYTESWAP4(x) +#define HTONS(x) BYTESWAP2(x) +#define NTOHS(x) BYTESWAP2(x) #else -#define HTONL(x) (x) -#define NTOHL(x) (x) -#define HTONS(x) (x) -#define NTOHS(x) (x) +#define HTONL(x) (x) +#define NTOHL(x) (x) +#define HTONS(x) (x) +#define NTOHS(x) (x) #endif // Print a HexDump #define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ - (len)).toHex()).toAscii().data()); + (len)).toHex()).toAscii().data()); /* ** RPC Header (8) -** - MSG_TYPE (2) -** - METHOD_ID (2) -** - LEN (4) [not including this header] +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] */ -#define PB_HDR_SIZE 8 +#define PB_HDR_SIZE 8 -#define PB_MSG_TYPE_REQUEST 1 -#define PB_MSG_TYPE_RESPONSE 2 -#define PB_MSG_TYPE_BINBLOB 3 +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 -#define MSGBUF_SIZE 4096 +#define MSGBUF_SIZE 4096 #endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 916cb95..b446c73 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -7,28 +7,28 @@ class QIODevice; class PbRpcController : public ::google::protobuf::RpcController { - bool failed; - QIODevice *blob; - std::string errStr; + bool failed; + QIODevice *blob; + std::string errStr; public: - PbRpcController() { Reset(); } + PbRpcController() { Reset(); } - // Client Side Methods - void Reset() { failed=false; blob = NULL; } - bool Failed() const { return failed; } - void StartCancel() { /*! \todo (MED) */} - std::string ErrorText() const { return errStr; } + // Client Side Methods + void Reset() { failed=false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } - // Server Side Methods - void SetFailed(const std::string &reason) - { failed = true; errStr = reason; } - bool IsCanceled() const { return false; }; - void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } - // srivatsp added - QIODevice* binaryBlob() { return blob; }; - void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; }; #endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 3d5a10c..804c4ee 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -3,254 +3,254 @@ RpcServer::RpcServer() { - server = NULL; - clientSock = NULL; + server = NULL; + clientSock = NULL; - service = NULL; + service = NULL; - isPending = false; - pendingMethodId = -1; // don't care as long as isPending is false + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false } RpcServer::~RpcServer() { - if (server) - delete server; + if (server) + delete server; } bool RpcServer::registerService(::google::protobuf::Service *service, - quint16 tcpPortNum) + quint16 tcpPortNum) { - this->service = service; + this->service = service; - server = new QTcpServer(); - connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); - if (!server->listen(QHostAddress::Any, tcpPortNum)) - { - qDebug("Unable to start the server: %s", - server->errorString().toAscii().constData()); - errorString_ = QString("Error starting Ostinato server: %1").arg( - server->errorString()); - return false; - } + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } - qDebug("The server is running on %s: %d", - server->serverAddress().toString().toAscii().constData(), - server->serverPort()); - errorString_ = QString(); - return true; + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; } QString RpcServer::errorString() { - return errorString_; + return errorString_; } void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) { - QIODevice *blob; - char msg[MSGBUF_SIZE]; - int len; + QIODevice *blob; + char msg[MSGBUF_SIZE]; + int len; - //qDebug("In RpcServer::done"); + //qDebug("In RpcServer::done"); - if (PbRpcController->Failed()) - { - qDebug("rpc failed"); - goto _exit; - } + if (PbRpcController->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } - blob = PbRpcController->binaryBlob(); - if (blob) - { - len = blob->size(); - qDebug("is binary blob of len %d", len); + blob = PbRpcController->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - (*(quint32*)(&msg[4])) = HTONL(len); // len + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + (*(quint32*)(&msg[4])) = HTONL(len); // len - clientSock->write(msg, PB_HDR_SIZE); + clientSock->write(msg, PB_HDR_SIZE); - blob->seek(0); - while (!blob->atEnd()) - { - int l; + blob->seek(0); + while (!blob->atEnd()) + { + int l; - len = blob->read(msg, sizeof(msg)); - l = clientSock->write(msg, len); - Q_ASSERT(l == len); - } + len = blob->read(msg, sizeof(msg)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } - goto _exit; - } + goto _exit; + } - if (!resp->IsInitialized()) - { - qWarning("response missing required fields!!"); - qDebug(resp->InitializationErrorString().c_str()); - qFatal("exiting"); - goto _exit; - } + if (!resp->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug(resp->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } - resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); + resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); - len = resp->ByteSize(); + len = resp->ByteSize(); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - *((quint32*)(&msg[4])) = HTONL(len); // len + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + *((quint32*)(&msg[4])) = HTONL(len); // len - // Avoid printing stats since it happens once every couple of seconds - if (pendingMethodId != 12) - { - qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); - //BUFDUMP(msg, len + 8); - } + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 12) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } - clientSock->write(msg, PB_HDR_SIZE + len); + clientSock->write(msg, PB_HDR_SIZE + len); _exit: - delete PbRpcController; - isPending = false; + delete PbRpcController; + isPending = false; } void RpcServer::when_newConnection() { - if (clientSock) - { - QTcpSocket *sock; + if (clientSock) + { + QTcpSocket *sock; - qDebug("already connected, no new connections will be accepted"); + qDebug("already connected, no new connections will be accepted"); - // Accept and close connection - //! \todo (MED) Send reason msg to client - sock = server->nextPendingConnection(); - sock->disconnectFromHost(); - sock->deleteLater(); - goto _exit; - } + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } - clientSock = server->nextPendingConnection(); - qDebug("accepting new connection from %s: %d", - clientSock->peerAddress().toString().toAscii().constData(), - clientSock->peerPort()); + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); - connect(clientSock, SIGNAL(readyRead()), - this, SLOT(when_dataAvail())); - connect(clientSock, SIGNAL(disconnected()), - this, SLOT(when_disconnected())); - connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(when_error(QAbstractSocket::SocketError))); + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); _exit: - return; + return; } void RpcServer::when_disconnected() { - qDebug("connection closed from %s: %d", - clientSock->peerAddress().toString().toAscii().constData(), - clientSock->peerPort()); + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); - clientSock->deleteLater(); - clientSock = NULL; + clientSock->deleteLater(); + clientSock = NULL; } void RpcServer::when_error(QAbstractSocket::SocketError socketError) { - qDebug("%s", clientSock->errorString().toAscii().constData()); + qDebug("%s", clientSock->errorString().toAscii().constData()); } void RpcServer::when_dataAvail() { - char msg[MSGBUF_SIZE]; - int msgLen; - static bool parsing = false; - static quint16 type, method; - static quint32 len; - const ::google::protobuf::MethodDescriptor *methodDesc; - ::google::protobuf::Message *req, *resp; - PbRpcController *controller; + char msg[MSGBUF_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; - if (!parsing) - { - if (clientSock->bytesAvailable() < PB_HDR_SIZE) - return; + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; - msgLen = clientSock->read(msg, PB_HDR_SIZE); + msgLen = clientSock->read(msg, PB_HDR_SIZE); - Q_ASSERT(msgLen == PB_HDR_SIZE); + Q_ASSERT(msgLen == PB_HDR_SIZE); - type = NTOHS(GET16(&msg[0])); - method = NTOHS(GET16(&msg[2])); - len = NTOHL(GET32(&msg[4])); - //qDebug("type = %d, method = %d, len = %d", type, method, len); + type = NTOHS(GET16(&msg[0])); + method = NTOHS(GET16(&msg[2])); + len = NTOHL(GET32(&msg[4])); + //qDebug("type = %d, method = %d, len = %d", type, method, len); - parsing = true; - } + parsing = true; + } - if (clientSock->bytesAvailable() < len) - return; + if (clientSock->bytesAvailable() < len) + return; - msgLen = clientSock->read(msg, sizeof(msg)); - Q_ASSERT((unsigned) msgLen == len); + msgLen = clientSock->read(msg, sizeof(msg)); + Q_ASSERT((unsigned) msgLen == len); - if (type != PB_MSG_TYPE_REQUEST) - { - qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, - type, PB_MSG_TYPE_REQUEST); - goto _error_exit; - } + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } - methodDesc = service->GetDescriptor()->method(method); - if (!methodDesc) - { - qDebug("server(%s): invalid method id %d", __FUNCTION__, method); - goto _error_exit; //! \todo Return Error to client - } + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } - if (isPending) - { - qDebug("server(%s): rpc pending, try again", __FUNCTION__); - goto _error_exit; //! \todo Return Error to client - } + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } - pendingMethodId = method; - isPending = true; + pendingMethodId = method; + isPending = true; - req = service->GetRequestPrototype(methodDesc).New(); - resp = service->GetResponsePrototype(methodDesc).New(); + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); - req->ParseFromArray((void*)msg, len); - if (!req->IsInitialized()) - { - qWarning("Missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); - qFatal("exiting"); - delete req; - delete resp; + req->ParseFromArray((void*)msg, len); + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; - goto _error_exit; - } - //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, - //resp->DebugString().c_str()); + goto _error_exit; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); - controller = new PbRpcController; + controller = new PbRpcController; - //qDebug("before service->callmethod()"); + //qDebug("before service->callmethod()"); - service->CallMethod(methodDesc, controller, req, resp, - NewCallback(this, &RpcServer::done, resp, controller)); + service->CallMethod(methodDesc, controller, req, resp, + NewCallback(this, &RpcServer::done, resp, controller)); - parsing = false; + parsing = false; - return; + return; _error_exit: - parsing = false; - qDebug("server(%s): discarding msg from client", __FUNCTION__); - return; + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; } diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 619cb2d..e353628 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -14,31 +14,31 @@ class RpcServer : public QObject { - Q_OBJECT + Q_OBJECT - QTcpServer *server; - QTcpSocket *clientSock; + QTcpServer *server; + QTcpSocket *clientSock; - ::google::protobuf::Service *service; + ::google::protobuf::Service *service; - bool isPending; - int pendingMethodId; - QString errorString_; + bool isPending; + int pendingMethodId; + QString errorString_; public: - RpcServer(); //! \todo (LOW) use 'parent' param - virtual ~RpcServer(); + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); - bool registerService(::google::protobuf::Service *service, - quint16 tcpPortNum); - QString errorString(); - void done(::google::protobuf::Message *resp, PbRpcController *controller); + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(::google::protobuf::Message *resp, PbRpcController *controller); private slots: - void when_newConnection(); - void when_disconnected(); - void when_dataAvail(); - void when_error(QAbstractSocket::SocketError socketError); + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); }; #endif diff --git a/server/drone.cpp b/server/drone.cpp index c84aec5..d7609de 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -19,58 +19,58 @@ Drone::Drone(QWidget *parent) Drone::~Drone() { - trayIcon_->hide(); - delete rpcServer; - delete service; + trayIcon_->hide(); + delete rpcServer; + delete service; } bool Drone::init() { - Q_ASSERT(rpcServer); + Q_ASSERT(rpcServer); if (!rpcServer->registerService(service, myport ? myport : 7878)) - { - QMessageBox::critical(0, qApp->applicationName(), - rpcServer->errorString()); - return false; - } + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } - trayIconMenu_ = new QMenu(this); + trayIconMenu_ = new QMenu(this); - trayIconMenu_->addAction(actionShow); - trayIconMenu_->addAction(actionExit); - trayIconMenu_->setDefaultAction(actionShow); - trayIcon_ = new QSystemTrayIcon(); - trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); - trayIcon_->setToolTip(qApp->applicationName()); - trayIcon_->setContextMenu(trayIconMenu_); - trayIcon_->show(); + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); - connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); - connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), - Qt::QueuedConnection); + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); - return true; + return true; } void Drone::changeEvent(QEvent *event) { - if (event->type() == QEvent::WindowStateChange && isMinimized()) - { - emit hideMe(true); - event->ignore(); - return; - } + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } - QWidget::changeEvent(event); + QWidget::changeEvent(event); } void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::DoubleClick) - { - showNormal(); - activateWindow(); - } + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } } diff --git a/server/drone.h b/server/drone.h index 5930dd0..c441af7 100644 --- a/server/drone.h +++ b/server/drone.h @@ -15,23 +15,23 @@ class Drone : public QWidget, Ui::Drone public: Drone(QWidget *parent = 0); - ~Drone(); + ~Drone(); bool init(); signals: - void hideMe(bool hidden); + void hideMe(bool hidden); protected: - void changeEvent(QEvent *event); + void changeEvent(QEvent *event); private: - QSystemTrayIcon *trayIcon_; - QMenu *trayIconMenu_; + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; RpcServer *rpcServer; OstProto::OstService *service; private slots: - void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); }; #endif diff --git a/server/drone.pro b/server/drone.pro index fe06b4e..a402b4c 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -15,12 +15,12 @@ RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui SOURCES += \ - drone_main.cpp \ - drone.cpp \ - portmanager.cpp \ - abstractport.cpp \ - pcapport.cpp \ - winpcapport.cpp + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp SOURCES += myservice.cpp SOURCES += pcapextra.cpp diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 6a9052f..f6baad2 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -5,21 +5,21 @@ int myport; int main(int argc, char *argv[]) { QApplication app(argc, argv); - Drone drone; + Drone drone; - app.setApplicationName(drone.objectName()); + app.setApplicationName(drone.objectName()); - if (argc > 1) - myport = atoi(argv[1]); + if (argc > 1) + myport = atoi(argv[1]); - if (!drone.init()) - exit(-1); + if (!drone.init()) + exit(-1); - drone.setWindowFlags(drone.windowFlags() - | Qt::WindowMaximizeButtonHint - | Qt::WindowMinimizeButtonHint); + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); drone.showMinimized(); app.exec(); - return 0; + return 0; } diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index 8274d48..b4fdba7 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -1,6 +1,6 @@ #include "pcapextra.h" -#include // memcpy() +#include // memcpy() #include // malloc(), free() /* NOTE: All code borrowed from WinPcap */ @@ -8,51 +8,51 @@ #ifndef Q_OS_WIN32 pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) { - pcap_send_queue *tqueue; + pcap_send_queue *tqueue; - /* Allocate the queue */ - tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); - if(tqueue == NULL){ - return NULL; - } + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } - /* Allocate the buffer */ - tqueue->buffer = (char*)malloc(memsize); - if(tqueue->buffer == NULL){ - free(tqueue); - return NULL; - } + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } - tqueue->maxlen = memsize; - tqueue->len = 0; + tqueue->maxlen = memsize; + tqueue->len = 0; - return tqueue; + return tqueue; } void pcap_sendqueue_destroy (pcap_send_queue *queue) { - free(queue->buffer); - free(queue); + free(queue->buffer); + free(queue); } int pcap_sendqueue_queue (pcap_send_queue *queue, - const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) { - if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > - queue->maxlen) - { - return -1; - } + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } - /* Copy the pcap_pkthdr header*/ - memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); - queue->len += sizeof(struct pcap_pkthdr); + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); - /* copy the packet */ - memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); - queue->len += pkt_header->caplen; + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; - return 0; + return 0; } #endif diff --git a/server/pcapextra.h b/server/pcapextra.h index e0dd23b..1f1c9f3 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -6,19 +6,19 @@ #ifndef Q_OS_WIN32 -//#define PCAP_OPENFLAG_PROMISCUOUS 1 +//#define PCAP_OPENFLAG_PROMISCUOUS 1 struct pcap_send_queue { - u_int maxlen; - u_int len; - char *buffer; + u_int maxlen; + u_int len; + char *buffer; }; pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); void pcap_sendqueue_destroy (pcap_send_queue *queue); int pcap_sendqueue_queue (pcap_send_queue *queue, - const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); #endif From a9e9a7db07cd0290aff7830a01099683caedd4a5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 28 Dec 2009 08:34:47 +0000 Subject: [PATCH 037/294] Removed vim modelines since we have a project .vimrc now --- server/abstractport.cpp | 2 -- server/abstractport.h | 2 -- server/myservice.cpp | 2 -- server/myservice.h | 2 -- server/pcapport.cpp | 2 -- server/pcapport.h | 2 -- server/portmanager.cpp | 3 --- server/portmanager.h | 2 -- server/winpcapport.cpp | 2 -- server/winpcapport.h | 2 -- 10 files changed, 21 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 8cbecd9..dfaf5b9 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -210,5 +210,3 @@ void AbstractPort::stats(PortStats *stats) stats->txPps = stats_.txPps; stats->txBps = stats_.txBps; } - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/abstractport.h b/server/abstractport.h index 6c0bcc3..e39d0f9 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -78,5 +78,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/myservice.cpp b/server/myservice.cpp index e742fe6..4da9b7e 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -419,5 +419,3 @@ void MyService::clearStats(::google::protobuf::RpcController* controller, done->Run(); } - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/myservice.h b/server/myservice.h index 5d434ee..a813367 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -81,5 +81,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 86a5086..a133812 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -403,5 +403,3 @@ QFile* PcapPort::PortCapturer::captureFile() { return &capFile_; } - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/pcapport.h b/server/pcapport.h index 8fee855..a17e665 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -117,5 +117,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/portmanager.cpp b/server/portmanager.cpp index bd7fd78..6105559 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -52,6 +52,3 @@ PortManager* PortManager::instance() return instance_; } - - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/portmanager.h b/server/portmanager.h index c71c6a6..1d4bd73 100644 --- a/server/portmanager.h +++ b/server/portmanager.h @@ -22,5 +22,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 7e04a42..4e64913 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -140,5 +140,3 @@ void WinPcapPort::PortMonitor::run() QThread::msleep(1000); } } - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/server/winpcapport.h b/server/winpcapport.h index 100ad2a..3f2a3b0 100644 --- a/server/winpcapport.h +++ b/server/winpcapport.h @@ -27,5 +27,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ From c5bcc2e0c2580ffa1f1e9ffbd38abec482e0b4e7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 28 Dec 2009 10:05:42 +0000 Subject: [PATCH 038/294] Converted all EOLs to unix-style '\n' only --- client/dumpview.cpp | 774 ++++++------- client/dumpview.h | 84 +- client/hexlineedit.cpp | 144 +-- client/hexlineedit.h | 48 +- client/main.cpp | 24 +- client/mainwindow.cpp | 130 +-- client/mainwindow.h | 66 +- client/ostinato.pro | 114 +- client/packetmodel.cpp | 440 +++---- client/packetmodel.h | 80 +- client/port.cpp | 394 +++---- client/port.h | 196 ++-- client/portgroup.cpp | 1436 +++++++++++------------ client/portgroup.h | 238 ++-- client/portgrouplist.cpp | 196 ++-- client/portgrouplist.h | 104 +- client/portmodel.cpp | 622 +++++----- client/portmodel.h | 102 +- client/portstatsfilterdialog.cpp | 222 ++-- client/portstatsfilterdialog.h | 70 +- client/portstatsmodel.cpp | 576 +++++----- client/portstatsmodel.h | 234 ++-- client/portstatswindow.cpp | 322 +++--- client/portstatswindow.h | 74 +- client/portswindow.cpp | 832 +++++++------- client/portswindow.h | 116 +- client/stream.cpp | 120 +- client/stream.h | 46 +- client/streamconfigdialog.cpp | 1850 +++++++++++++++--------------- client/streamconfigdialog.h | 226 ++-- client/streammodel.cpp | 484 ++++---- client/streammodel.h | 114 +- common/protocol.proto | 420 +++---- rpc/pbhelper.h | 302 ++--- rpc/pbrpc.pro | 16 +- rpc/pbrpcchannel.cpp | 620 +++++----- rpc/pbrpcchannel.h | 166 +-- rpc/pbrpccommon.h | 122 +- rpc/pbrpccontroller.h | 68 +- rpc/rpcserver.cpp | 512 ++++----- rpc/rpcserver.h | 88 +- server/drone.cpp | 152 +-- server/drone.h | 74 +- server/drone.pro | 52 +- server/drone_main.cpp | 50 +- server/myservice.cpp | 842 +++++++------- 46 files changed, 6981 insertions(+), 6981 deletions(-) diff --git a/client/dumpview.cpp b/client/dumpview.cpp index d846164..0056502 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -1,387 +1,387 @@ -#include "dumpview.h" - -//! \todo Enable Scrollbars - -DumpView::DumpView(QWidget *parent) -{ - int w, h; - - // 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 - // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ - 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"); -} - -QModelIndex DumpView::indexAt( const QPoint &point ) const -{ -#if 0 - int x = point.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 = point.y()/mLineHeight; - - if ((col < 16) && (row < ((data.size()+16)/16))) - { - selrow = row; - selcol = 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)", selrow, selcol); - - offset = selrow * 16 + selcol; -#if 0 - for(int i = 0; i < model()->rowCount(parent); i++) - { - QModelIndex index = model()->index(i, 0, parent); - - if (model()->hasChildren(index)) - indexAtOffset(offset, index); // Non Leaf - else - if ( - dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf - // FIXME: Use RawValueRole instead of UserRole - } -#endif -} - -_exit: - // Clear existing selection - selrow = -1; - -#endif - return QModelIndex(); -} - -void DumpView::scrollTo( const QModelIndex &index, ScrollHint hint ) -{ - // FIXME: implement scrolling -} - -QRect DumpView::visualRect( const QModelIndex &index ) const -{ - // FIXME: calculate actual rect - return rect(); -} - -//protected: -int DumpView::horizontalOffset() const -{ - return horizontalScrollBar()->value(); -} - -bool DumpView::isIndexHidden( const QModelIndex &index ) const -{ - return false; -} - -QModelIndex DumpView::moveCursor( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ) -{ - // FIXME(MED): need to implement movement using cursor - return currentIndex(); -} - -void DumpView::setSelection( const QRect &rect, - QItemSelectionModel::SelectionFlags flags ) -{ - // FIXME(HI): calculate indexes using rect - selectionModel()->select(QModelIndex(), flags); -} - -int DumpView::verticalOffset() const -{ - return verticalScrollBar()->value(); -} - -QRegion DumpView::visualRegionForSelection( const QItemSelection &selection ) const -{ - // FIXME(HI) - return QRegion(rect()); -} - -//protected slots: -void DumpView::dataChanged( const QModelIndex &topLeft, - const QModelIndex &bottomRight ) -{ - // FIXME(HI) - update(); -} - -void DumpView::selectionChanged( const QItemSelection &selected, - const QItemSelection &deselected ) -{ - // FIXME(HI) - update(); -} - -void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, - QModelIndex parent) -{ - // FIXME: Use new enum instead of Qt::UserRole - //! \todo (low): generalize this for any model not just our pkt model - - Q_ASSERT(!parent.isValid()); - - qDebug("!!!! %d $$$$", dump.size()); - - for(int i = 0; i < model()->rowCount(parent); i++) - { - QModelIndex index = model()->index(i, 0, parent); - - Q_ASSERT(index.isValid()); - - // Assumption: protocol data is in bytes (not bits) - qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); - dump.append(model()->data(index, Qt::UserRole).toByteArray()); - - } - - if (selectionModel()->selectedIndexes().size()) - { - int j, bits; - QModelIndex index; - - Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); - index = selectionModel()->selectedIndexes().at(0); - - if (index.parent().isValid()) - { - // Field - - // SelOfs = SUM(protocol sizes before selected field's protocol) + - // SUM(field sizes before selected field) - - selOfs = 0; - j = index.parent().row() - 1; - while (j >= 0) - { - selOfs += model()->data(index.parent().sibling(j,0), - Qt::UserRole).toByteArray().size(); - j--; - } - - bits = 0; - j = index.row() - 1; - while (j >= 0) - { - bits += model()->data(index.sibling(j,0), Qt::UserRole+1). - toInt(); - j--; - } - selOfs += bits/8; - selSize = model()->data(index, Qt::UserRole).toByteArray().size(); - } - else - { - // Protocol - selOfs = 0; - j = index.row() - 1; - while (j >= 0) - { - selOfs += model()->data(index.sibling(j,0), Qt::UserRole). - toByteArray().size(); - j--; - } - selSize = model()->data(index, Qt::UserRole).toByteArray().size(); - } - } -} - -// TODO(LOW): rewrite this function - it's a mess! -void DumpView::paintEvent(QPaintEvent* event) -{ - QStylePainter painter(viewport()); - QRect offsetRect = mOffsetPaneTopRect; - QRect dumpRect = mDumpPaneTopRect; - QRect asciiRect = mAsciiPaneTopRect; - QPalette pal = palette(); - static QByteArray data; - //QByteArray ba; - int selOfs = -1, selSize; - int curSelOfs, curSelSize; - - 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))); - - if (model()) - { - data.clear(); - populateDump(data, selOfs, selSize); - } - - // 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); - - // if no selection, skip selection painting - if (selOfs < 0) - goto _next; - - // Check overlap between current row and selection - { - QRect r1(i, 0, qMin(16, data.size()-i), 8); - QRect s1(selOfs, 0, selSize, 8); - if (r1.intersects(s1)) - { - QRect t = r1.intersected(s1); - - curSelOfs = t.x(); - curSelSize = t.width(); - } - else - curSelSize = 0; - - } - - // overpaint selection on current row (if any) - if (curSelSize > 0) - { - QRect r; - QString selectedAsciiStr, selectedDumpStr; - - qDebug("dumpview::paintEvent - Highlighted (%d, %d)", - curSelOfs, curSelSize); - - // construct the dumpStr and asciiStr - for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) - { - unsigned char c = data.at(k); - - // extra space after 8 bytes - if (((k+8) % 16) == 0) - { - // Avoid adding space at the start for fields starting - // at second column 8 byte boundary - if (k!=curSelOfs) - { - selectedDumpStr.append(" "); - selectedAsciiStr.append(" "); - } - } - - selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, - QChar('0')).toUpper()).append(" "); - - if (isPrintable(c)) - selectedAsciiStr.append(QChar(c)); - else - selectedAsciiStr.append(QChar('.')); - } - - // display dump - r = dumpRect; - if ((curSelOfs - i) < 8) - r.translate(mCharWidth*(curSelOfs-i)*3, 0); - else - r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); - - // adjust width taking care of selection stretching between - // the two 8byte columns - if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) - r.setWidth((curSelSize * 3 + 1) * mCharWidth); - else - r.setWidth((curSelSize * 3) * mCharWidth); - - painter.fillRect(r, pal.highlight()); - painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, - true, selectedDumpStr, QPalette::HighlightedText); - - // display ascii - r = asciiRect; - if ((curSelOfs - i) < 8) - r.translate(mCharWidth*(curSelOfs-i)*1, 0); - else - r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); - - // adjust width taking care of selection stretching between - // the two 8byte columns - if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) - r.setWidth((curSelSize * 1 + 1) * mCharWidth); - else - r.setWidth((curSelSize * 1) * mCharWidth); - - painter.fillRect(r, pal.highlight()); - painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, - true, selectedAsciiStr, QPalette::HighlightedText); - } - -_next: - // move the rects down - offsetRect.translate(0, mLineHeight); - dumpRect.translate(0, mLineHeight); - asciiRect.translate(0, mLineHeight); - } -} - +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt( const QPoint &point ) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo( const QModelIndex &index, ScrollHint hint ) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect( const QModelIndex &index ) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden( const QModelIndex &index ) const +{ + return false; +} + +QModelIndex DumpView::moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection( const QRect &rect, + QItemSelectionModel::SelectionFlags flags ) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( const QItemSelection &selection ) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* event) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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 index db17002..6f7db2e 100644 --- a/client/dumpview.h +++ b/client/dumpview.h @@ -1,42 +1,42 @@ -#include // FIXME: High - - -class DumpView: public QAbstractItemView -{ -public: - DumpView(QWidget *parent=0); - - QModelIndex indexAt( const QPoint &point ) const; - void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); - QRect visualRect( const QModelIndex &index ) const; - -protected: - int horizontalOffset() const; - bool isIndexHidden( const QModelIndex &index ) const; - QModelIndex moveCursor( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ); - void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); - int verticalOffset() const; - QRegion visualRegionForSelection( const QItemSelection &selection ) const; -protected slots: - void dataChanged( const QModelIndex &topLeft, - const QModelIndex &bottomRight ); - void selectionChanged( const QItemSelection &selected, - const QItemSelection &deselected ); - void paintEvent(QPaintEvent *event); - -private: - void populateDump(QByteArray &dump, int &selOfs, int &selSize, - QModelIndex parent = QModelIndex()); - bool inline isPrintable(char c) - {if ((c > 48) && (c < 126)) return true; else return false; } - -private: - QRect mOffsetPaneTopRect; - QRect mDumpPaneTopRect; - QRect mAsciiPaneTopRect; - int mSelectedRow, mSelectedCol; - int mLineHeight; - int mCharWidth; -}; - +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index 5f099d0..4497710 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -1,72 +1,72 @@ -#include "hexlineedit.h" -#include "qdebug.h" - -QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); - -HexLineEdit::HexLineEdit( QWidget * parent) - : QLineEdit(parent) -{ - //QLineEdit::QLineEdit(parent); -} - -void HexLineEdit::focusOutEvent( QFocusEvent *e ) -{ -#if 0 - const QValidator *v = validator(); - if ( v ) - { - int curpos = cursorPosition(); - QString str = text(); - if ( v->validate( str, curpos ) == QValidator::Acceptable ) - { - if ( curpos != cursorPosition() ) - setCursorPosition( curpos ); - if ( str != text() ) - setText( str ); - } - else - { - if ( curpos != cursorPosition() ) - setCursorPosition( curpos ); - str = text(); - v->fixup( str ); - if ( str != text() ) - { - setText( str ); - } - } - } - QLineEdit::focusOutEvent( e ); - emit focusOut(); -#else -#define uintToHexStr(num, bytesize) \ - QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) - - bool isOk; - ulong num; - - qDebug("before = %s\n", text().toAscii().data()); - num = text().remove(QChar(' ')).toULong(&isOk, 16); - setText(uintToHexStr(num, 4)); - qDebug("after = %s\n", text().toAscii().data()); -#undef uintToHexStr -#endif -} - -#if 0 -void HexLineEdit::focusInEvent( QFocusEvent *e ) -{ - QLineEdit::focusInEvent( e ); - emit focusIn(); -} - -void HexLineEdit::keyPressEvent( QKeyEvent *e ) -{ - QLineEdit::keyPressEvent( e ); - if ( e->key() == Key_Enter || e->key() == Key_Return ) - { - setSelection( 0, text().length() ); - } -} -#endif - +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent( QFocusEvent *e ) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h index 937d263..09f638a 100644 --- a/client/hexlineedit.h +++ b/client/hexlineedit.h @@ -1,24 +1,24 @@ -#ifndef _HEXLINEEDIT -#define _HEXLINEEDIT - -#include - -class HexLineEdit : public QLineEdit -{ - Q_OBJECT -public: - // Constructors - HexLineEdit ( QWidget * parent); - -protected: - void focusOutEvent( QFocusEvent *e ); - //void focusInEvent( QFocusEvent *e ); - //void keyPressEvent( QKeyEvent *e ); - -signals: - //void focusIn(); - void focusOut(); -}; - -#endif - +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/main.cpp b/client/main.cpp index 9a9a5d8..994952a 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,12 +1,12 @@ -#include "mainwindow.h" - -#include - -int main(int argc, char* argv[]) -{ - QApplication app(argc, argv); - MainWindow mainWin; - - mainWin.show(); - return app.exec(); -} +#include "mainwindow.h" + +#include + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + MainWindow mainWin; + + mainWin.show(); + return app.exec(); +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 2aee434..0f818c5 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -1,65 +1,65 @@ -#include "mainwindow.h" - -#if 0 -#include "dbgthread.h" -#endif - -#include "portgrouplist.h" -#include "portstatswindow.h" -#include "portswindow.h" -#include "ui_about.h" - -#include -#include - -PortGroupList *pgl; - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow (parent) -{ - localServer_ = new QProcess(this); - localServer_->start("drone.exe"); - - pgl = new PortGroupList; - - portsWindow = new PortsWindow(pgl, this); - statsWindow = new PortStatsWindow(pgl, this); - portsDock = new QDockWidget(tr("Ports"), this); - statsDock = new QDockWidget(tr("Stats"), this); - - setupUi(this); - - statsDock->setWidget(statsWindow); - addDockWidget(Qt::BottomDockWidgetArea, statsDock); - portsDock->setWidget(portsWindow); - addDockWidget(Qt::TopDockWidgetArea, portsDock); - - connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); -#if 0 - { - DbgThread *dbg = new DbgThread(pgl); - dbg->start(); - } -#endif -} - -MainWindow::~MainWindow() -{ - delete pgl; - localServer_->terminate(); - localServer_->waitForFinished(); - delete localServer_; -} - -void MainWindow::on_actionHelpAbout_triggered() -{ - QDialog *aboutDialog = new QDialog; - - Ui::About about; - about.setupUi(aboutDialog); - - aboutDialog->exec(); - - delete aboutDialog; -} - +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "ui_about.h" + +#include +#include + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + localServer_ = new QProcess(this); + localServer_->start("drone.exe"); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports"), this); + statsDock = new QDockWidget(tr("Stats"), this); + + setupUi(this); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h index 53c8eac..9ddd926 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -1,33 +1,33 @@ -#ifndef _MAIN_WINDOW_H -#define _MAIN_WINDOW_H - -#include "ui_mainwindow.h" -#include - -class PortsWindow; -class PortStatsWindow; - -class QDockWidget; -class QProcess; - -class MainWindow : public QMainWindow, private Ui::MainWindow -{ - Q_OBJECT - -private: - QProcess *localServer_; - PortsWindow *portsWindow; - PortStatsWindow *statsWindow; - QDockWidget *portsDock; - QDockWidget *statsDock; - -public: - MainWindow(QWidget *parent = 0); - ~MainWindow(); - -public slots: - void on_actionHelpAbout_triggered(); -}; - -#endif - +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/ostinato.pro b/client/ostinato.pro index b0dd570..3afbdc2 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,57 +1,57 @@ -TEMPLATE = app -CONFIG += qt debug -QT += network script -INCLUDEPATH += "../rpc/" "../common/" -LIBS += -lprotobuf -win32:LIBS += -L"../common/debug" -lostproto -unix: LIBS += -L"../common" -lostproto -win32:LIBS += -L"../rpc/debug" -lpbrpc -unix:LIBS += -L"../rpc" -lpbrpc -POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" -RESOURCES += ostinato.qrc -HEADERS += \ - dumpview.h \ - hexlineedit.h \ - mainwindow.h \ - packetmodel.h \ - port.h \ - portgroup.h \ - portgrouplist.h \ - portmodel.h \ - portstatsmodel.h \ - portstatsfilterdialog.h \ - portstatswindow.h \ - portswindow.h \ - streamconfigdialog.h \ - streamlistdelegate.h \ - streammodel.h - -FORMS += \ - about.ui \ - mainwindow.ui \ - portstatsfilter.ui \ - portstatswindow.ui \ - portswindow.ui \ - streamconfigdialog.ui - -SOURCES += \ - dumpview.cpp \ - stream.cpp \ - hexlineedit.cpp \ - main.cpp \ - mainwindow.cpp \ - packetmodel.cpp \ - port.cpp \ - portgroup.cpp \ - portgrouplist.cpp \ - portmodel.cpp \ - portstatsmodel.cpp \ - portstatsfilterdialog.cpp \ - portstatswindow.cpp \ - portswindow.cpp \ - streamconfigdialog.cpp \ - streamlistdelegate.cpp \ - streammodel.cpp - -# TODO(LOW): Test only -include(modeltest.pri) +TEMPLATE = app +CONFIG += qt debug +QT += network script +INCLUDEPATH += "../rpc/" "../common/" +LIBS += -lprotobuf +win32:LIBS += -L"../common/debug" -lostproto +unix: LIBS += -L"../common" -lostproto +win32:LIBS += -L"../rpc/debug" -lpbrpc +unix:LIBS += -L"../rpc" -lpbrpc +POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + +# TODO(LOW): Test only +include(modeltest.pri) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index a3aee4d..df5a30b 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,220 +1,220 @@ -#include - -#include "packetmodel.h" -#include "../common/protocollistiterator.h" -#include "../common/abstractprotocol.h" - -PacketModel::PacketModel(QObject *parent) -{ -} - -void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) -{ - QList currentProtocols; - - iter.toFront(); - while (iter.hasNext()) - currentProtocols.append(iter.next()); - - if (mSelectedProtocols != currentProtocols) - { - mSelectedProtocols = currentProtocols; - reset(); - } -} - -int PacketModel::rowCount(const QModelIndex &parent) const -{ - IndexId parentId; - - // qDebug("in %s", __FUNCTION__); - - // Parent == Invalid i.e. Invisible Root. - // ==> Children are Protocol (Top Level) Items - if (!parent.isValid()) - return mSelectedProtocols.size(); - - // Parent - Valid Item - parentId.w = parent.internalId(); - switch(parentId.ws.type) - { - case ITYP_PROTOCOL: - return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); - case ITYP_FIELD: - return 0; - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - - Q_ASSERT(1 == 0); // Unreachable code - qWarning("%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; - - if (!hasIndex(row, col, parent)) - goto _exit; - - // Parent is Invisible Root - // Request for a Protocol Item - if (!parent.isValid()) - { - id.w = 0; - id.ws.type = ITYP_PROTOCOL; - id.ws.protocol = row; - index = createIndex(row, col, id.w); - goto _exit; - } - - // Parent is a Valid Item - parentId.w = parent.internalId(); - id.w = parentId.w; - switch(parentId.ws.type) - { - case ITYP_PROTOCOL: - id.ws.type = ITYP_FIELD; - index = createIndex(row, col, id.w); - goto _exit; - - case ITYP_FIELD: - Q_ASSERT(1 == 0); // Unreachable code - goto _exit; - - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - - Q_ASSERT(1 == 0); // Unreachable code - -_exit: - return index; -} - -QModelIndex PacketModel::parent(const QModelIndex &index) const -{ - QModelIndex parentIndex; - IndexId id, parentId; - - if (!index.isValid()) - return QModelIndex(); - - id.w = index.internalId(); - parentId.w = id.w; - switch(id.ws.type) - { - case ITYP_PROTOCOL: - // return invalid index for invisible root - goto _exit; - - case ITYP_FIELD: - parentId.ws.type = ITYP_PROTOCOL; - parentIndex = createIndex(id.ws.protocol, 0, parentId.w); - goto _exit; - - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - - Q_ASSERT(1 == 1); // Unreachable code - -_exit: - return parentIndex; -} - -QVariant PacketModel::data(const QModelIndex &index, int role) const -{ - IndexId id; - int fieldIdx = 0; - - if (!index.isValid()) - return QVariant(); - - id.w = index.internalId(); - - if (id.ws.type == ITYP_FIELD) - { - const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); - int n = index.row() + 1; - - while (n) - { - if (!(p->fieldFlags(fieldIdx).testFlag( - AbstractProtocol::FieldIsMeta))) - n--; - fieldIdx++; - } - fieldIdx--; - } - - // FIXME(HI): Relook at this completely - if (role == Qt::UserRole) - { - switch(id.ws.type) - { - case ITYP_PROTOCOL: - qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); - return mSelectedProtocols.at(id.ws.protocol)-> - protocolFrameValue(); - - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData( - fieldIdx, AbstractProtocol::FieldFrameValue); - - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - return QByteArray(); - } - - // FIXME: Use a new enum here instead of UserRole - if (role == (Qt::UserRole+1)) - { - switch(id.ws.type) - { - case ITYP_PROTOCOL: - return mSelectedProtocols.at(id.ws.protocol)-> - protocolFrameValue().size(); - - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData( - fieldIdx, AbstractProtocol::FieldBitSize); - - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - return QVariant(); - } - - if (role != Qt::DisplayRole) - return QVariant(); - - switch(id.ws.type) - { - case ITYP_PROTOCOL: - return QString("%1 (%2)") - .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) - .arg(mSelectedProtocols.at(id.ws.protocol)->name()); - - case ITYP_FIELD: - return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, - AbstractProtocol::FieldName).toString() + QString(" : ") + - mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, - AbstractProtocol::FieldTextValue).toString(); - - default: - qWarning("%s: Unhandled ItemType", __FUNCTION__); - } - - Q_ASSERT(1 == 1); // Unreachable code - - return QVariant(); -} +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (!(p->fieldFlags(fieldIdx).testFlag( + AbstractProtocol::FieldIsMeta))) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h index 2568724..cd81a8f 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -1,40 +1,40 @@ -#ifndef _PACKET_MODEL_H -#define _PACKET_MODEL_H - -#include - -class ProtocolListIterator; -class AbstractProtocol; - -class PacketModel: public QAbstractItemModel -{ - -public: - PacketModel(QObject *parent = 0); - void setSelectedProtocols(ProtocolListIterator &iter); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const { return QVariant(); } ; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; - -private: - typedef union _IndexId - { - quint32 w; - struct - { - quint16 type; -#define ITYP_PROTOCOL 1 -#define ITYP_FIELD 2 - quint16 protocol; // protocol is valid for both ITYPs - } ws; - } IndexId; - - QList mSelectedProtocols; -}; -#endif - +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const { return QVariant(); } ; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp index 8f708f1..2509927 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -1,197 +1,197 @@ -#include - -#include - -#include "port.h" -#include "pbhelper.h" - -uint Port::mAllocStreamId = 0; - -uint Port::newStreamId() -{ - return mAllocStreamId++; -} - -Port::Port(quint32 id, quint32 portGroupId) -{ - mPortId = id; - d.mutable_port_id()->set_id(id); - stats.mutable_port_id()->set_id(id); - mPortGroupId = portGroupId; -} - -Port::~Port() -{ -} - -void Port::updatePortConfig(OstProto::Port *port) -{ - d.MergeFrom(*port); -} - -void Port::updateStreamOrdinalsFromIndex() -{ - for (int i=0; i < mStreams.size(); i++) - mStreams[i]->setOrdinal(i); -} - -void Port::reorderStreamsByOrdinals() -{ - qSort(mStreams); -} - -bool Port::newStreamAt(int index) -{ - Stream *s = new Stream; - - if (index > mStreams.size()) - return false; - - s->setId(newStreamId()); - mStreams.insert(index, s); - updateStreamOrdinalsFromIndex(); - - return true; -} - -bool Port::deleteStreamAt(int index) -{ - if (index >= mStreams.size()) - return false; - - delete mStreams.takeAt(index); - updateStreamOrdinalsFromIndex(); - - return true; -} - -bool Port::insertStream(uint streamId) -{ - Stream *s = new Stream; - - s->setId(streamId); - - // FIXME(MED): If a stream with id already exists, what do we do? - mStreams.append(s); - - // Update mAllocStreamId to take into account the stream id received - // from server - if (mAllocStreamId <= streamId) - mAllocStreamId = streamId + 1; - - return true; -} - -bool Port::updateStream(uint streamId, OstProto::Stream *stream) -{ - int i, streamIndex; - - for (i = 0; i < mStreams.size(); i++) - { - if (streamId == mStreams[i]->id()) - goto _found; - } - - qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); - return false; - -_found: - streamIndex = i; - - mStreams[streamIndex]->protoDataCopyFrom(*stream); - reorderStreamsByOrdinals(); - - return true; -} - -void Port::getDeletedStreamsSinceLastSync( - OstProto::StreamIdList &streamIdList) -{ - streamIdList.clear_stream_id(); - for (int i = 0; i < mLastSyncStreamList.size(); i++) - { - int j; - - for (j = 0; j < mStreams.size(); j++) - { - if (mLastSyncStreamList[i] == mStreams[j]->id()) - break; - } - - if (j < mStreams.size()) - { - // stream still exists! - continue; - } - else - { - // stream has been deleted since last sync - OstProto::StreamId *s; - - s = streamIdList.add_stream_id(); - s->set_id(mLastSyncStreamList.at(i)); - } - } -} - -void Port::getNewStreamsSinceLastSync( - OstProto::StreamIdList &streamIdList) -{ - streamIdList.clear_stream_id(); - for (int i = 0; i < mStreams.size(); i++) - { - if (mLastSyncStreamList.contains(mStreams[i]->id())) - { - // existing stream! - continue; - } - else - { - // new stream! - OstProto::StreamId *s; - - s = streamIdList.add_stream_id(); - s->set_id(mStreams[i]->id()); - } - } -} - -void Port::getModifiedStreamsSinceLastSync( - OstProto::StreamConfigList &streamConfigList) -{ - qDebug("In %s", __FUNCTION__); - - //streamConfigList.mutable_port_id()->set_id(mPortId); - for (int i = 0; i < mStreams.size(); i++) - { - OstProto::Stream *s; - - s = streamConfigList.add_stream(); - mStreams[i]->protoDataCopyInto(*s); - } - qDebug("Done %s", __FUNCTION__); -} - -void Port::when_syncComplete() -{ - qSort(mStreams); - - mLastSyncStreamList.clear(); - for (int i=0; iid()); -} - -void Port::updateStats(OstProto::PortStats *portStats) -{ - OstProto::PortState oldState; - - oldState = stats.state(); - stats.MergeFrom(*portStats); - - if (oldState.link_state() != stats.state().link_state()) - { - qDebug("portstate changed"); - emit portDataChanged(mPortGroupId, mPortId); - } -} - +#include + +#include + +#include "port.h" +#include "pbhelper.h" + +uint Port::mAllocStreamId = 0; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; +} + +Port::~Port() +{ +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams); +} + +bool Port::newStreamAt(int index) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + qSort(mStreams); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + diff --git a/client/port.h b/client/port.h index 09e025a..53842a5 100644 --- a/client/port.h +++ b/client/port.h @@ -1,98 +1,98 @@ -#ifndef _PORT_H -#define _PORT_H - -#include -#include -#include -#include "stream.h" - -//class StreamModel; - -class Port : public QObject { - - Q_OBJECT - - static uint mAllocStreamId; - OstProto::Port d; - OstProto::PortStats stats; - - // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' - quint32 mPortId; - quint32 mPortGroupId; - QString mUserAlias; // user defined - - QList mLastSyncStreamList; - QList mStreams; // sorted by stream's ordinal value - - uint newStreamId(); - void updateStreamOrdinalsFromIndex(); - void reorderStreamsByOrdinals(); - -public: - enum AdminStatus { AdminDisable, AdminEnable }; - enum ControlMode { ControlShared, ControlExclusive }; - - // FIXME(HIGH): default args is a hack for QList operations on Port - Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); - ~Port(); - - quint32 portGroupId() const { return mPortGroupId; } - const QString& userAlias() const { return mUserAlias; } - - quint32 id() const - { return d.port_id().id(); } - const QString name() const - { return QString().fromStdString(d.name()); } - const QString description() const - { return QString().fromStdString(d.description()); } - AdminStatus adminStatus() - { return (d.is_enabled()?AdminEnable:AdminDisable); } - ControlMode controlMode() - { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } - - //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } - void setAlias(QString &alias) { mUserAlias = alias; } - //void setExclusive(bool flag); - - int numStreams() { return mStreams.size(); } - Stream* streamByIndex(int index) - { - Q_ASSERT(index < mStreams.size()); - return mStreams[index]; - } - OstProto::LinkState linkState() - { return stats.state().link_state(); } - - OstProto::PortStats getStats() { return stats; } - - // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal - void updatePortConfig(OstProto::Port *port); - - //! Used by StreamModel - //@{ - bool newStreamAt(int index); - bool deleteStreamAt(int index); - //@} - - //! Used by MyService::Stub to update from config received from server - //@{ - bool insertStream(uint streamId); - bool updateStream(uint streamId, OstProto::Stream *stream); - //@} - - void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); - void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); - void getModifiedStreamsSinceLastSync( - OstProto::StreamConfigList &streamConfigList); - - - void when_syncComplete(); - - void updateStats(OstProto::PortStats *portStats); - -signals: - void portDataChanged(int portGroupId, int portId); - -}; - -#endif +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + OstProto::Port d; + OstProto::PortStats stats; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + enum ControlMode { ControlShared, ControlExclusive }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + ControlMode controlMode() + { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + + void when_syncComplete(); + + void updateStats(OstProto::PortStats *portStats); + +signals: + void portDataChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp index e26d2a1..4ec94c3 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,718 +1,718 @@ -#include -#include - -#include "portgroup.h" - - -quint32 PortGroup::mPortGroupAllocId = 0; - -PortGroup::PortGroup(QHostAddress ip, quint16 port) -{ - // Allocate an id for self - mPortGroupId = PortGroup::mPortGroupAllocId++; - - rpcChannel = new PbRpcChannel(ip, port); - - /*! - \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation - as implemented currently, if a RPC is invoked before the previous completes, - rpc controller is overwritten due to the Reset() call - maybe we need to pass the - pointer to the controller to the callback function also? - */ - rpcController = new PbRpcController; - rpcControllerStats = new PbRpcController; - isGetStatsPending_ = false; - serviceStub = new OstProto::OstService::Stub(rpcChannel, - OstProto::OstService::STUB_OWNS_CHANNEL); - - // FIXME(LOW):Can't for my life figure out why this ain't working! - //QMetaObject::connectSlotsByName(this); - connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(on_rpcChannel_stateChanged())); - connect(rpcChannel, SIGNAL(connected()), - this, SLOT(on_rpcChannel_connected())); - connect(rpcChannel, SIGNAL(disconnected()), - this, SLOT(on_rpcChannel_disconnected())); - connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); -} - -PortGroup::~PortGroup() -{ - qDebug("PortGroup Destructor"); - // Disconnect and free rpc channel etc. - PortGroup::disconnectFromHost(); - delete serviceStub; -} - - -// ------------------------------------------------ -// Slots -// ------------------------------------------------ -void PortGroup::on_rpcChannel_stateChanged() -{ - qDebug("state changed"); - emit portGroupDataChanged(mPortGroupId); -} - -void PortGroup::on_rpcChannel_connected() -{ - OstProto::Void void_; - OstProto::PortIdList *portIdList; - - qDebug("connected\n"); - emit portGroupDataChanged(mPortGroupId); - - qDebug("requesting portlist ..."); - portIdList = new OstProto::PortIdList(); - rpcController->Reset(); - serviceStub->getPortIdList(rpcController, &void_, portIdList, - NewCallback(this, &PortGroup::processPortIdList, portIdList)); -} - -void PortGroup::on_rpcChannel_disconnected() -{ - qDebug("disconnected\n"); - emit portListAboutToBeChanged(mPortGroupId); - - while (!mPorts.isEmpty()) - delete mPorts.takeFirst(); - - emit portListChanged(mPortGroupId); - emit portGroupDataChanged(mPortGroupId); -} - -void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) -{ - qDebug("error\n"); - emit portGroupDataChanged(mPortGroupId); -} - -void PortGroup::when_configApply(int portIndex, uint *cookie) -{ - uint *op; - OstProto::Ack *ack; - - Q_ASSERT(portIndex < mPorts.size()); - - if (state() != QAbstractSocket::ConnectedState) - { - if (cookie != NULL) - delete cookie; - return; - } - - if (cookie == NULL) - { - // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] - // cookie[1]: *ack - cookie = new uint[2]; - ack = new OstProto::Ack; - - cookie[0] = (uint) 0; - cookie[1] = (uint) ack; - } - else - { - ack = (OstProto::Ack*) cookie[1]; - } - - Q_ASSERT(cookie != NULL); - op = &cookie[0]; - - switch (*op) - { - case 0: - { - OstProto::StreamIdList streamIdList; - - qDebug("applying 'deleted streams' ..."); - - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); - - (*op)++; - rpcController->Reset(); - serviceStub->deleteStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 1: - { - OstProto::StreamIdList streamIdList; - - qDebug("applying 'new streams' ..."); - - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); - - (*op)++; - rpcController->Reset(); - serviceStub->addStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 2: - { - OstProto::StreamConfigList streamConfigList; - - qDebug("applying 'modified streams' ..."); - - streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); - - (*op)++; - rpcController->Reset(); - serviceStub->modifyStream(rpcController, &streamConfigList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 3: - qDebug("apply completed"); - mPorts[portIndex]->when_syncComplete(); - delete cookie; - break; - - default: - qDebug("%s: Unknown Op!!!", __FUNCTION__); - break; - } -} - -void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) -{ - qDebug("got a portlist ..."); - - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } - - emit portListAboutToBeChanged(mPortGroupId); - - for(int i = 0; i < portIdList->port_id_size(); i++) - { - Port *p; - - p = new Port(portIdList->port_id(i).id(), mPortGroupId); - connect(p, SIGNAL(portDataChanged(int, int)), - this, SIGNAL(portGroupDataChanged(int, int))); - qDebug("before port append\n"); - mPorts.append(p); - } - - emit portListChanged(mPortGroupId); - - this->portIdList.CopyFrom(*portIdList); - - // Request PortConfigList - { - OstProto::PortConfigList *portConfigList; - - qDebug("requesting port config list ..."); - portConfigList = new OstProto::PortConfigList(); - rpcController->Reset(); - serviceStub->getPortConfig(rpcController, - portIdList, portConfigList, NewCallback(this, - &PortGroup::processPortConfigList, portConfigList)); - } - - goto _exit; - -_error_exit: -_exit: - delete portIdList; -} - -void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) -{ - qDebug("In %s", __FUNCTION__); - - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } - - emit portListAboutToBeChanged(mPortGroupId); - - for(int i = 0; i < portConfigList->port_size(); i++) - { - uint id; - - id = portConfigList->port(i).port_id().id(); - // FIXME: don't mix port id & index into mPorts[] - mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); - } - - emit portListChanged(mPortGroupId); - - // FIXME: check if we need new signals since we are not changing the - // number of ports, just the port data - - if (numPorts() > 0) - getStreamIdList(); - -_error_exit: - delete portConfigList; -} - -void PortGroup::getStreamIdList(int portIndex, - OstProto::StreamIdList *streamIdList) -{ - ::OstProto::PortId portId; - - qDebug("In %s", __FUNCTION__); - - if (streamIdList == NULL) - { - // First invocation (uses default params) - - // request StreamIdList for first port - - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); - streamIdList = new ::OstProto::StreamIdList(); - - goto _request; - } - - qDebug("got a streamIdlist ..."); - - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; // FIXME(MED): Partial RPC - } - - Q_ASSERT(portIndex < numPorts()); - - if (streamIdList->port_id().id() != mPorts[portIndex]->id()) - { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), - portIndex); - goto _next_port; // FIXME(MED): Partial RPC - } - - // FIXME(MED): need to mPorts.clear()??? - for(int i = 0; i < streamIdList->stream_id_size(); i++) - { - uint streamId; - - streamId = streamIdList->stream_id(i).id(); - mPorts[portIndex]->insertStream(streamId); - } - -_next_port: - // FIXME(HI): ideally we shd use signals/slots but this means - // we will have to use Port* instead of Port with QList<> - - // need to find a way for this - mPorts[portIndex]->when_syncComplete(); - portIndex++; - if (portIndex >= numPorts()) - { - // We're done for all ports !!! - - // FIXME(HI): some way to reset streammodel - - delete streamIdList; - - if (numPorts() > 0) - getStreamConfigList(); - - goto _exit; - } - -_request: - portId.set_id(mPorts[portIndex]->id()); - streamIdList->Clear(); - - rpcController->Reset(); - serviceStub->getStreamIdList(rpcController, &portId, streamIdList, - NewCallback(this, &PortGroup::getStreamIdList, - portIndex, streamIdList)); - - goto _exit; - - - -_exit: - return; -} - -void PortGroup::getStreamConfigList(int portIndex, - OstProto::StreamConfigList *streamConfigList) -{ - OstProto::StreamIdList streamIdList; - - qDebug("In %s", __PRETTY_FUNCTION__); - - if (streamConfigList == NULL) - { - // First invocation using default params - // - request for first port - - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); - - streamConfigList = new OstProto::StreamConfigList; - - goto _request; - } - - qDebug("got a streamconfiglist"); - - if (rpcController->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; - } - - Q_ASSERT(portIndex < numPorts()); - - if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) - { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamConfigList->port_id().id(), - mPorts[portIndex]->id(), portIndex); - goto _next_port; // FIXME(MED): Partial RPC - } - - // FIXME(MED): need to mStreams.clear()??? - for(int i = 0; i < streamConfigList->stream_size(); i++) - { - uint streamId; - - streamId = streamConfigList->stream(i).stream_id().id(); - mPorts[portIndex]->updateStream(streamId, - streamConfigList->mutable_stream(i)); - } - -_next_port: - portIndex++; - - if (portIndex >= numPorts()) - { - // We're done for all ports !!! - - // FIXME(HI): some way to reset streammodel - - delete streamConfigList; - goto _exit; - } - -_request: - qDebug("requesting stream config list ..."); - - streamIdList.Clear(); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) - { - OstProto::StreamId *s; - - s = streamIdList.add_stream_id(); - s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); - } - streamConfigList->Clear(); - - rpcController->Reset(); - serviceStub->getStreamConfig(rpcController, - &streamIdList, streamConfigList, NewCallback(this, - &PortGroup::getStreamConfigList, portIndex, streamConfigList)); - -_exit: - return; -} - -void PortGroup::processModifyStreamAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - qDebug("Modify Successful!!"); - - // TODO(HI): Apply Button should now be disabled???!!!!??? -} - -void PortGroup::startTx(QList *portList) -{ - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - return; - - ack = new OstProto::Ack; - if (portList == NULL) - goto _exit; - else - { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - } - - serviceStub->startTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartTxAck, ack)); -_exit: - return; -} - -void PortGroup::stopTx(QList *portList) -{ - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - goto _exit; - - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; - - ack = new OstProto::Ack; - - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - - rpcController->Reset(); - serviceStub->stopTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopTxAck, ack)); -_exit: - return; -} - -void PortGroup::startCapture(QList *portList) -{ - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - return; - - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; - - ack = new OstProto::Ack; - - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - - rpcController->Reset(); - serviceStub->startCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartCaptureAck, ack)); -_exit: - return; -} - -void PortGroup::stopCapture(QList *portList) -{ - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - return; - - if ((portList == NULL) || (portList->size() == 0)) - goto _exit; - - ack = new OstProto::Ack; - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - - rpcController->Reset(); - serviceStub->stopCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopCaptureAck, ack)); -_exit: - return; -} - -void PortGroup::viewCapture(QList *portList) -{ - static QTemporaryFile *capFile = NULL; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - goto _exit; - - if ((portList == NULL) || (portList->size() != 1)) - goto _exit; - - if (capFile) - delete capFile; - - /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is - not working - it fails everytime */ - capFile = new QTemporaryFile(); - capFile->open(); - qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); - - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId portId; - OstProto::CaptureBuffer *buf; - - portId.set_id(portList->at(i)); - - buf = new OstProto::CaptureBuffer; - rpcController->Reset(); - rpcController->setBinaryBlob(capFile); - serviceStub->getCaptureBuffer(rpcController, &portId, buf, - NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); - } -_exit: - return; -} - -void PortGroup::processStartTxAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processStopTxAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processStartCaptureAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processStopCaptureAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) -{ - qDebug("In %s", __FUNCTION__); - - capFile->flush(); - capFile->close(); - - if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", - QStringList() << capFile->fileName())) - qDebug("Failed starting Wireshark"); - - delete buf; -} - -void PortGroup::getPortStats() -{ - OstProto::PortStatsList *portStatsList; - - //qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - return; - - if (isGetStatsPending_) - return; - - portStatsList = new OstProto::PortStatsList; - rpcControllerStats->Reset(); - isGetStatsPending_ = true; - serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, - NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); -} - -void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) -{ - //qDebug("In %s", __FUNCTION__); - - if (rpcControllerStats->Failed()) - { - qDebug("%s: rpc failed", __FUNCTION__); - goto _error_exit; - } - - for(int i = 0; i < portStatsList->port_stats_size(); i++) - { - uint id; - - id = portStatsList->port_stats(i).port_id().id(); - // FIXME: don't mix port id & index into mPorts[] - mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); - } - - emit statsChanged(mPortGroupId); - -_error_exit: - delete portStatsList; - isGetStatsPending_ = false; -} - -void PortGroup::clearPortStats(QList *portList) -{ - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - - qDebug("In %s", __FUNCTION__); - - if (state() != QAbstractSocket::ConnectedState) - return; - - ack = new OstProto::Ack; - if (portList == NULL) - portIdList.CopyFrom(this->portIdList); - else - { - for (int i = 0; i < portList->size(); i++) - { - OstProto::PortId *portId; - - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } - } - - rpcController->Reset(); - serviceStub->clearStats(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processClearStatsAck, ack)); -} - -void PortGroup::processClearStatsAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - // Refresh stats immediately after a stats clear/reset - getPortStats(); - - delete ack; -} - +#include +#include + +#include "portgroup.h" + + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + rpcChannel = new PbRpcChannel(ip, port); + + /*! + \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation + as implemented currently, if a RPC is invoked before the previous completes, + rpc controller is overwritten due to the Reset() call - maybe we need to pass the + pointer to the controller to the callback function also? + */ + rpcController = new PbRpcController; + rpcControllerStats = new PbRpcController; + isGetStatsPending_ = false; + serviceStub = new OstProto::OstService::Stub(rpcChannel, + OstProto::OstService::STUB_OWNS_CHANNEL); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged())); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_rpcChannel_stateChanged() +{ + qDebug("state changed"); + emit portGroupDataChanged(mPortGroupId); +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void void_; + OstProto::PortIdList *portIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + qDebug("requesting portlist ..."); + portIdList = new OstProto::PortIdList(); + rpcController->Reset(); + serviceStub->getPortIdList(rpcController, &void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, portIdList)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("error\n"); + emit portGroupDataChanged(mPortGroupId); +} + +void PortGroup::when_configApply(int portIndex, uint *cookie) +{ + uint *op; + OstProto::Ack *ack; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + { + if (cookie != NULL) + delete cookie; + return; + } + + if (cookie == NULL) + { + // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] + // cookie[1]: *ack + cookie = new uint[2]; + ack = new OstProto::Ack; + + cookie[0] = (uint) 0; + cookie[1] = (uint) ack; + } + else + { + ack = (OstProto::Ack*) cookie[1]; + } + + Q_ASSERT(cookie != NULL); + op = &cookie[0]; + + switch (*op) + { + case 0: + { + OstProto::StreamIdList streamIdList; + + qDebug("applying 'deleted streams' ..."); + + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); + + (*op)++; + rpcController->Reset(); + serviceStub->deleteStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 1: + { + OstProto::StreamIdList streamIdList; + + qDebug("applying 'new streams' ..."); + + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); + + (*op)++; + rpcController->Reset(); + serviceStub->addStream(rpcController, &streamIdList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 2: + { + OstProto::StreamConfigList streamConfigList; + + qDebug("applying 'modified streams' ..."); + + streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); + + (*op)++; + rpcController->Reset(); + serviceStub->modifyStream(rpcController, &streamConfigList, ack, + ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); + break; + } + + case 3: + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + delete cookie; + break; + + default: + qDebug("%s: Unknown Op!!!", __FUNCTION__); + break; + } +} + +void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) +{ + qDebug("got a portlist ..."); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + this->portIdList.CopyFrom(*portIdList); + + // Request PortConfigList + { + OstProto::PortConfigList *portConfigList; + + qDebug("requesting port config list ..."); + portConfigList = new OstProto::PortConfigList(); + rpcController->Reset(); + serviceStub->getPortConfig(rpcController, + portIdList, portConfigList, NewCallback(this, + &PortGroup::processPortConfigList, portConfigList)); + } + + goto _exit; + +_error_exit: +_exit: + delete portIdList; +} + +void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) +{ + qDebug("In %s", __FUNCTION__); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete portConfigList; +} + +void PortGroup::getStreamIdList(int portIndex, + OstProto::StreamIdList *streamIdList) +{ + ::OstProto::PortId portId; + + qDebug("In %s", __FUNCTION__); + + if (streamIdList == NULL) + { + // First invocation (uses default params) - + // request StreamIdList for first port + + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); + streamIdList = new ::OstProto::StreamIdList(); + + goto _request; + } + + qDebug("got a streamIdlist ..."); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; // FIXME(MED): Partial RPC + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), + portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } + + // FIXME(MED): need to mPorts.clear()??? + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + +_next_port: + // FIXME(HI): ideally we shd use signals/slots but this means + // we will have to use Port* instead of Port with QList<> - + // need to find a way for this + mPorts[portIndex]->when_syncComplete(); + portIndex++; + if (portIndex >= numPorts()) + { + // We're done for all ports !!! + + // FIXME(HI): some way to reset streammodel + + delete streamIdList; + + if (numPorts() > 0) + getStreamConfigList(); + + goto _exit; + } + +_request: + portId.set_id(mPorts[portIndex]->id()); + streamIdList->Clear(); + + rpcController->Reset(); + serviceStub->getStreamIdList(rpcController, &portId, streamIdList, + NewCallback(this, &PortGroup::getStreamIdList, + portIndex, streamIdList)); + + goto _exit; + + + +_exit: + return; +} + +void PortGroup::getStreamConfigList(int portIndex, + OstProto::StreamConfigList *streamConfigList) +{ + OstProto::StreamIdList streamIdList; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (streamConfigList == NULL) + { + // First invocation using default params + // - request for first port + + Q_ASSERT(portIndex == 0); + Q_ASSERT(numPorts() > 0); + + streamConfigList = new OstProto::StreamConfigList; + + goto _request; + } + + qDebug("got a streamconfiglist"); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _next_port; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", + __FUNCTION__, streamConfigList->port_id().id(), + mPorts[portIndex]->id(), portIndex); + goto _next_port; // FIXME(MED): Partial RPC + } + + // FIXME(MED): need to mStreams.clear()??? + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + +_next_port: + portIndex++; + + if (portIndex >= numPorts()) + { + // We're done for all ports !!! + + // FIXME(HI): some way to reset streammodel + + delete streamConfigList; + goto _exit; + } + +_request: + qDebug("requesting stream config list ..."); + + streamIdList.Clear(); + streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + streamConfigList->Clear(); + + rpcController->Reset(); + serviceStub->getStreamConfig(rpcController, + &streamIdList, streamConfigList, NewCallback(this, + &PortGroup::getStreamConfigList, portIndex, streamConfigList)); + +_exit: + return; +} + +void PortGroup::processModifyStreamAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("Modify Successful!!"); + + // TODO(HI): Apply Button should now be disabled???!!!!??? +} + +void PortGroup::startTx(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + goto _exit; + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->startTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, ack)); +_exit: + return; +} + +void PortGroup::stopTx(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + ack = new OstProto::Ack; + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + + rpcController->Reset(); + serviceStub->stopTx(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, ack)); +_exit: + return; +} + +void PortGroup::startCapture(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + ack = new OstProto::Ack; + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + + rpcController->Reset(); + serviceStub->startCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, ack)); +_exit: + return; +} + +void PortGroup::stopCapture(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + ack = new OstProto::Ack; + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + + rpcController->Reset(); + serviceStub->stopCapture(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, ack)); +_exit: + return; +} + +void PortGroup::viewCapture(QList *portList) +{ + static QTemporaryFile *capFile = NULL; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + if (capFile) + delete capFile; + + /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is + not working - it fails everytime */ + capFile = new QTemporaryFile(); + capFile->open(); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId portId; + OstProto::CaptureBuffer *buf; + + portId.set_id(portList->at(i)); + + buf = new OstProto::CaptureBuffer; + rpcController->Reset(); + rpcController->setBinaryBlob(capFile); + serviceStub->getCaptureBuffer(rpcController, &portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processStopTxAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processStartCaptureAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processStopCaptureAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + delete ack; +} + +void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) +{ + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", + QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + + delete buf; +} + +void PortGroup::getPortStats() +{ + OstProto::PortStatsList *portStatsList; + + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if (isGetStatsPending_) + return; + + portStatsList = new OstProto::PortStatsList; + rpcControllerStats->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, + NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); +} + +void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) +{ + //qDebug("In %s", __FUNCTION__); + + if (rpcControllerStats->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList->port_stats_size(); i++) + { + uint id; + + id = portStatsList->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + delete portStatsList; + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + OstProto::PortIdList portIdList; + OstProto::Ack *ack; + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + ack = new OstProto::Ack; + if (portList == NULL) + portIdList.CopyFrom(this->portIdList); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId; + + portId = portIdList.add_port_id(); + portId->set_id(portList->at(i)); + } + } + + rpcController->Reset(); + serviceStub->clearStats(rpcController, &portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, ack)); +} + +void PortGroup::processClearStatsAck(OstProto::Ack *ack) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete ack; +} + diff --git a/client/portgroup.h b/client/portgroup.h index a503910..4566b46 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -1,119 +1,119 @@ -#ifndef _PORT_GROUP_H -#define _PORT_GROUP_H - -#include "port.h" -#include -#include - -#include "../common/protocol.pb.h" -#include "pbrpcchannel.h" - -/* TODO -HIGH -MED -LOW -- Allow hostnames in addition to IP Address as "server address" -*/ - -#define DEFAULT_SERVER_PORT 7878 - -class QFile; - -class PortGroup : public QObject { - Q_OBJECT - -private: - quint32 mPortGroupId; - static quint32 mPortGroupAllocId; - QString mUserAlias; // user defined -#if 0 // PB - QTcpSocket *mpSocket; - QHostAddress mServerAddress; - quint16 mServerPort; -#endif - PbRpcChannel *rpcChannel; - PbRpcController *rpcController; - PbRpcController *rpcControllerStats; - bool isGetStatsPending_; - ::OstProto::OstService::Stub *serviceStub; - - ::OstProto::PortIdList portIdList; -public: // FIXME(HIGH): member access - QList mPorts; - -public: - PortGroup(QHostAddress ip = QHostAddress::LocalHost, - quint16 port = DEFAULT_SERVER_PORT); - ~PortGroup(); - - void connectToHost() { rpcChannel->establish(); } - void connectToHost(QHostAddress ip, quint16 port) - { rpcChannel->establish(ip, port); } - void disconnectFromHost() { rpcChannel->tearDown(); } - - int numPorts() const { return mPorts.size(); } - quint32 id() const { return mPortGroupId; } - - const QString& userAlias() const { return mUserAlias; } - void setUserAlias(QString alias) { mUserAlias = alias; }; - - const QHostAddress& serverAddress() const - { return rpcChannel->serverAddress(); } - quint16 serverPort() const - { return rpcChannel->serverPort(); } - QAbstractSocket::SocketState state() const - { return rpcChannel->state(); } - - void processPortIdList(OstProto::PortIdList *portIdList); - void processPortConfigList(OstProto::PortConfigList *portConfigList); - - void getStreamIdList(int portIndex = 0, - OstProto::StreamIdList *streamIdList = NULL); - void getStreamConfigList(int portIndex = 0, - OstProto::StreamConfigList *streamConfigList = NULL); - - void processModifyStreamAck(OstProto::Ack *ack); - - void startTx(QList *portList = NULL); - void processStartTxAck(OstProto::Ack *ack); - void stopTx(QList *portList = NULL); - void processStopTxAck(OstProto::Ack *ack); - - void startCapture(QList *portList = NULL); - void processStartCaptureAck(OstProto::Ack *ack); - void stopCapture(QList *portList = NULL); - void processStopCaptureAck(OstProto::Ack *ack); - void viewCapture(QList *portList = NULL); - void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); - - void getPortStats(); - void processPortStatsList(OstProto::PortStatsList *portStatsList); - void clearPortStats(QList *portList = NULL); - void processClearStatsAck(OstProto::Ack *ack); - -signals: - void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); - void portListAboutToBeChanged(quint32 portGroupId); - void portListChanged(quint32 portGroupId); - void statsChanged(quint32 portGroupId); - -private slots: - void on_rpcChannel_stateChanged(); - void on_rpcChannel_connected(); - void on_rpcChannel_disconnected(); - void on_rpcChannel_error(QAbstractSocket::SocketError socketError); - -public slots: - void when_configApply(int portIndex, uint *cookie = NULL); -#if 0 // PB - void on_rpcChannel_when_dataAvail(); -#endif - -private: -#if 0 // PB - void ProcessCapabilityInfo(const char *msg, qint32 size); - void ProcessMsg(const char *msg, quint32 size); -#endif -}; - -#endif +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; + +class PortGroup : public QObject { + Q_OBJECT + +private: + quint32 mPortGroupId; + static quint32 mPortGroupAllocId; + QString mUserAlias; // user defined +#if 0 // PB + QTcpSocket *mpSocket; + QHostAddress mServerAddress; + quint16 mServerPort; +#endif + PbRpcChannel *rpcChannel; + PbRpcController *rpcController; + PbRpcController *rpcControllerStats; + bool isGetStatsPending_; + ::OstProto::OstService::Stub *serviceStub; + + ::OstProto::PortIdList portIdList; +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { rpcChannel->establish(ip, port); } + void disconnectFromHost() { rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(OstProto::PortIdList *portIdList); + void processPortConfigList(OstProto::PortConfigList *portConfigList); + + void getStreamIdList(int portIndex = 0, + OstProto::StreamIdList *streamIdList = NULL); + void getStreamConfigList(int portIndex = 0, + OstProto::StreamConfigList *streamConfigList = NULL); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(OstProto::Ack *ack); + void stopTx(QList *portList = NULL); + void processStopTxAck(OstProto::Ack *ack); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(OstProto::Ack *ack); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(OstProto::Ack *ack); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); + + void getPortStats(); + void processPortStatsList(OstProto::PortStatsList *portStatsList); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(OstProto::Ack *ack); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_rpcChannel_stateChanged(); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + +public slots: + void when_configApply(int portIndex, uint *cookie = NULL); +#if 0 // PB + void on_rpcChannel_when_dataAvail(); +#endif + +private: +#if 0 // PB + void ProcessCapabilityInfo(const char *msg, qint32 size); + void ProcessMsg(const char *msg, quint32 size); +#endif +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 3e67d01..49aa539 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -1,98 +1,98 @@ -#include "portgrouplist.h" - -// TODO(LOW): Remove -#include - -PortGroupList::PortGroupList() - : mPortGroupListModel(this), - mStreamListModel(this), - mPortStatsModel(this, this) -{ - PortGroup *pg; - - // TODO(LOW): Remove - new ModelTest(getStreamModel()); - new ModelTest(getPortModel()); - new ModelTest(getPortStatsModel()); - - // Add the "Local" Port Group - pg = new PortGroup; - addPortGroup(*pg); -} - -bool PortGroupList::isPortGroup(const QModelIndex& index) -{ - return mPortGroupListModel.isPortGroup(index); -} - -bool PortGroupList::isPort(const QModelIndex& index) -{ - return mPortGroupListModel.isPort(index); -} - -PortGroup& PortGroupList::portGroup(const QModelIndex& index) -{ - Q_ASSERT(mPortGroupListModel.isPortGroup(index)); - - return *(mPortGroups[index.row()]); -} - -Port& PortGroupList::port(const QModelIndex& index) -{ - Q_ASSERT(mPortGroupListModel.isPort(index)); - return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); -} - -void PortGroupList::addPortGroup(PortGroup &portGroup) -{ - mPortGroupListModel.portGroupAboutToBeAppended(); - - connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), - &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); -#if 0 - connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), - &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortGroupListModel, SLOT(triggerLayoutChanged())); -#endif - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortGroupListModel, SLOT(when_portListChanged())); - - connect(&portGroup, SIGNAL(portListChanged(quint32)), - &mPortStatsModel, SLOT(when_portListChanged())); - - connect(&portGroup, SIGNAL(statsChanged(quint32)), - &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); - - mPortGroups.append(&portGroup); - portGroup.connectToHost(); - - mPortGroupListModel.portGroupAppended(); - - mPortStatsModel.when_portListChanged(); -} - -void PortGroupList::removePortGroup(PortGroup &portGroup) -{ - mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); - - PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); - qDebug("after takeAt()"); - mPortGroupListModel.portGroupRemoved(); - - delete pg; - - mPortStatsModel.when_portListChanged(); -} - -//.................... -// Private Methods -//.................... -int PortGroupList::indexOfPortGroup(quint32 portGroupId) -{ - for (int i = 0; i < mPortGroups.size(); i++) { - if (mPortGroups.value(i)->id() == portGroupId) - return i; - } - return -1; -} +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + + // TODO(LOW): Remove + new ModelTest(getStreamModel()); + new ModelTest(getPortModel()); + new ModelTest(getPortStatsModel()); + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h index e5e398b..3dd8f5f 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -1,52 +1,52 @@ -#ifndef _PORT_GROUP_LIST_H -#define _PORT_GROUP_LIST_H - -#include "portgroup.h" -#include -#include -#include "portmodel.h" -#include "streammodel.h" -#include "portstatsmodel.h" - -class PortModel; -class StreamModel; - -class PortGroupList : public QObject { - - Q_OBJECT - - friend class PortModel; - friend class StreamModel; - friend class PortStatsModel; - - QList mPortGroups; - PortModel mPortGroupListModel; - StreamModel mStreamListModel; - PortStatsModel mPortStatsModel; - -// Methods -public: - PortGroupList(); - - - PortModel* getPortModel() { return &mPortGroupListModel; } - PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } - StreamModel* getStreamModel() { return &mStreamListModel; } - - bool isPortGroup(const QModelIndex& index); - bool isPort(const QModelIndex& index); - PortGroup& portGroup(const QModelIndex& index); - Port& port(const QModelIndex& index); - - int numPortGroups() { return mPortGroups.size(); } - PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } - - void addPortGroup(PortGroup &portGroup); - void removePortGroup(PortGroup &portGroup); - -private: - int indexOfPortGroup(quint32 portGroupId); - -}; - -#endif +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + +// Methods +public: + PortGroupList(); + + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp index ff5c482..6ab422e 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -1,311 +1,311 @@ -#include "portmodel.h" -#include "portgrouplist.h" -#include - -#if 0 -#define DBG0(x) qDebug(x) -#define DBG1(x, p1) qDebug(x, (p1)) -#else -#define DBG0(x) {} -#define DBG1(x, p1) {} -#endif - -PortModel::PortModel(PortGroupList *p, QObject *parent) - : QAbstractItemModel(parent) -{ - pgl = p; -} - -int PortModel::rowCount(const QModelIndex &parent) const -{ - // qDebug("RowCount Enter\n"); - if (!parent.isValid()) - { - // Top Level Item - //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); - return pgl->mPortGroups.size(); - } - // qDebug("RowCount non top %d, %d, %llx\n", - // parent.row(), parent.column(), parent.internalId()); - - quint16 pg = (parent.internalId() >> 16) & 0xFFFF; - quint16 p = parent.internalId() & 0xFFFF; - if (p == 0xFFFF) - { -#if 0 // wrong code? - int count = 0; - foreach(PortGroup *pg, pgl->mPortGroups) - { - count += pg->numPorts(); - } - //qDebug("RowCount (Mid) Exit: %d\n", count); - return count; -#endif - if (parent.column() == 0) - return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); - else - return 0; - } - else - { - // Leaf Item - return 0; - } -} - -int PortModel::columnCount(const QModelIndex &parent ) const -{ - return 1; // FIXME: hardcoding -} - -Qt::ItemFlags PortModel::flags(const QModelIndex &index) const -{ - return QAbstractItemModel::flags(index); // FIXME: no need for this func -} -QVariant PortModel::data(const QModelIndex &index, int role) const -{ - - DBG0("Enter PortModel data\n"); - - // Check for a valid index - if (!index.isValid()) - return QVariant(); - - DBG1("PortModel::data(index).row = %d", index.row()); - DBG1("PortModel::data(index).column = %0d", index.column()); - DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); - - QModelIndex parent = index.parent(); - - if (!parent.isValid()) - { - // Top Level Item - PortGroup - if ((role == Qt::DisplayRole)) - { - DBG0("Exit PortModel data 1\n"); - return QString("Port Group %1: %2 [%3:%4] (%5)"). - arg(pgl->mPortGroups.at(index.row())->id()). - arg(pgl->mPortGroups.at(index.row())->userAlias()). - arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). - arg(pgl->mPortGroups.at(index.row())->serverPort()). - arg(pgl->mPortGroups.value(index.row())->numPorts()); - } - else if ((role == Qt::DecorationRole)) - { - DBG0("Exit PortModel data 2\n"); - switch(pgl->mPortGroups.at(index.row())->state()) - { - case QAbstractSocket::UnconnectedState: - return QIcon(":/icons/bullet_red.png"); - - case QAbstractSocket::HostLookupState: - return QIcon(":/icons/bullet_yellow.png"); - - case QAbstractSocket::ConnectingState: - case QAbstractSocket::ClosingState: - return QIcon(":/icons/bullet_orange.png"); - - case QAbstractSocket::ConnectedState: - return QIcon(":/icons/bullet_green.png"); - - - case QAbstractSocket::BoundState: - case QAbstractSocket::ListeningState: - default: - return QIcon(":/icons/bullet_error.png"); - } - } - else - { - DBG0("Exit PortModel data 3\n"); - return QVariant(); - } - } - else - { - // Non Top Level - Port - if ((role == Qt::DisplayRole)) - { - DBG0("Exit PortModel data 4\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); - - return QString("Port %1: %2 [%3] (%4)"). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->id()). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->name()). - arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->description()); - } - else if ((role == Qt::DecorationRole)) - { - DBG0("Exit PortModel data 5\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); - switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) - { - case OstProto::LinkStateUnknown: - return QIcon(":/icons/bullet_white.png"); - case OstProto::LinkStateDown: - return QIcon(":/icons/bullet_red.png"); - case OstProto::LinkStateUp: - return QIcon(":/icons/bullet_green.png"); - default: - qFatal("unexpected/unimplemented port oper state"); - } - } - else - { - DBG0("Exit PortModel data 6\n"); - return QVariant(); - } - } - - return QVariant(); -} - -QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) - return QVariant(); - else - return QString("Name"); -} - -QModelIndex PortModel::index (int row, int col, - const QModelIndex & parent) const -{ - if (!hasIndex(row, col, parent)) - return QModelIndex(); - - //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", - // row, col, parent.row(), parent.column(), parent.internalId()); - - if (!parent.isValid()) - { - // Top Level Item - quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; - quint32 id = (pg << 16) | p; - //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - - return createIndex(row, col, id); - } - else - { - quint16 pg = parent.internalId() >> 16; - quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); - quint32 id = (pg << 16) | p; - //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); - - return createIndex(row, col, id); - } -} - -QModelIndex PortModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - //qDebug("parent: R=%d, C=%d ID=%llx\n", - // index.row(), index.column(), index.internalId()); - - quint16 pg = index.internalId() >> 16; - quint16 p = index.internalId() & 0x0000FFFF; - - //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); - - if (p == 0xFFFF) - { - //qDebug("parent ret: NULL\n"); - // Top Level Item - PG - return QModelIndex(); - } - - quint32 id = (pg << 16) | 0xFFFF; - //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); - - return createIndex(pgl->indexOfPortGroup(pg), 0, id); - -} - -bool PortModel::isPortGroup(const QModelIndex& index) -{ - if ((index.internalId() & 0xFFFF) == 0xFFFF) - return true; - else - return false; -} - -bool PortModel::isPort(const QModelIndex& index) -{ - if ((index.internalId() & 0xFFFF) != 0xFFFF) - return true; - else - return false; -} - -quint32 PortModel::portGroupId(const QModelIndex& index) -{ - return (index.internalId()) >> 16 & 0xFFFF; -} - -quint32 PortModel::portId(const QModelIndex& index) -{ - return (index.internalId()) & 0xFFFF; -} - - - -// ---------------------------------------------- -// Slots -// ---------------------------------------------- -void PortModel::when_portGroupDataChanged(int portGroupId, int portId) -{ - QModelIndex index; - int row; - - if (portId == 0xFFFF) - row = pgl->indexOfPortGroup(portGroupId); - else - row = portId; - - index = createIndex(row, 0, (portGroupId << 16) | portId); - - emit dataChanged(index, index); -} - -void PortModel::portGroupAboutToBeAppended() -{ - int row; - - row = pgl->mPortGroups.size(); - beginInsertRows(QModelIndex(), row, row); -} - -void PortModel::portGroupAppended() -{ - endInsertRows(); -} - -void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) -{ - int row; - - row = pgl->mPortGroups.indexOf(portGroup); - beginRemoveRows(QModelIndex(), row, row); -} - -void PortModel::portGroupRemoved() -{ - endRemoveRows(); -} - -void PortModel::when_portListChanged() -{ - reset(); -} +#include "portmodel.h" +#include "portgrouplist.h" +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &parent ) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 4\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); + + return QString("Port %1: %2 [%3] (%4)"). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->id()). + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->name()). + arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) + arg(pgl->mPortGroups.at( + parent.row())->mPorts[index.row()]->description()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 5\n"); + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + return QVariant(); + switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) + { + case OstProto::LinkStateUnknown: + return QIcon(":/icons/bullet_white.png"); + case OstProto::LinkStateDown: + return QIcon(":/icons/bullet_red.png"); + case OstProto::LinkStateUp: + return QIcon(":/icons/bullet_green.png"); + default: + qFatal("unexpected/unimplemented port oper state"); + } + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if ((index.internalId() & 0xFFFF) == 0xFFFF) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if ((index.internalId() & 0xFFFF) != 0xFFFF) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h index eea9763..7f3b821 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -1,51 +1,51 @@ -#ifndef _PORT_MODEL_H -#define _PORT_MODEL_H - -#include - -class PortGroupList; -class PortGroup; - -class PortModel : public QAbstractItemModel -{ - Q_OBJECT - - friend class PortGroupList; - - PortGroupList *pgl; - - public: - PortModel(PortGroupList *p, QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; - - bool isPortGroup(const QModelIndex& index); - bool isPort(const QModelIndex& index); - quint32 portGroupId(const QModelIndex& index); - quint32 portId(const QModelIndex& index); - - -private slots: - void when_portGroupDataChanged(int portGroupId, int portId); - - void portGroupAboutToBeAppended(); - void portGroupAppended(); - void portGroupAboutToBeRemoved(PortGroup *portGroup); - void portGroupRemoved(); - - void when_portListChanged(); - -#if 0 - void triggerLayoutAboutToBeChanged(); - void triggerLayoutChanged(); -#endif - -}; - -#endif +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + + PortGroupList *pgl; + + public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif + +}; + +#endif diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index b21bd37..4291fea 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -1,111 +1,111 @@ -#include "portstatsfilterdialog.h" - -PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) -{ - setupUi(this); - - 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); - item->setFlags(Qt::ItemIsSelectable - | Qt::ItemIsDragEnabled - //| Qt::ItemIsDropEnabled - | Qt::ItemIsEnabled); - - 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; - while (lvUnselected->selectionModel()->selectedIndexes().size()) - { - item = mUnselected.takeItem(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mUnselected.removeRow(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row())) - mSelected.appendRow(item); - } -} - -void PortStatsFilterDialog::on_tbSelectOut_clicked() -{ - QStandardItem *item; - - while (lvSelected->selectionModel()->selectedIndexes().size()) - { - item = mSelected.takeItem(lvSelected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mSelected.removeRow(lvSelected->selectionModel()-> - selectedIndexes().at(0).row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } - } -} - -void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) -{ - QStandardItem *item; - - item = mUnselected.takeItem(lvUnselected->currentIndex().row()); - if (mUnselected.removeRow(lvUnselected->currentIndex().row())) - mSelected.appendRow(item); -} - -void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) -{ - QStandardItem *item; - - item = mSelected.takeItem(lvSelected->currentIndex().row()); - if (mSelected.removeRow(lvSelected->currentIndex().row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } -} - +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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; + while (lvUnselected->selectionModel()->selectedIndexes().size()) + { + item = mUnselected.takeItem(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mUnselected.removeRow(lvUnselected->selectionModel()-> + selectedIndexes().at(0).row())) + mSelected.appendRow(item); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QStandardItem *item; + + while (lvSelected->selectionModel()->selectedIndexes().size()) + { + item = mSelected.takeItem(lvSelected->selectionModel()-> + selectedIndexes().at(0).row()); + if (mSelected.removeRow(lvSelected->selectionModel()-> + selectedIndexes().at(0).row())) + { + mUnselected.appendRow(item); + mUnselected.sort(0); + } + } +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QStandardItem *item; + + item = mUnselected.takeItem(lvUnselected->currentIndex().row()); + if (mUnselected.removeRow(lvUnselected->currentIndex().row())) + mSelected.appendRow(item); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + 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 ce8016c..28d94ed 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -1,35 +1,35 @@ -#ifndef _PORT_STATS_FILTER_DIALOG_H -#define _PORT_STATS_FILTER_DIALOG_H - -#include -#include -#include -#include "ui_portstatsfilter.h" -#include "portgrouplist.h" - -class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog -{ - Q_OBJECT - -public: - 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(); - void on_lvUnselected_doubleClicked(const QModelIndex &index); - void on_lvSelected_doubleClicked(const QModelIndex &index); -}; - -#endif - +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 5aec2b6..58993e4 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -1,288 +1,288 @@ -#include "portstatsmodel.h" -#include "portgrouplist.h" - -#include - -PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) - : QAbstractTableModel(parent) -{ - QTimer *timer; - - pgl = p; - - timer = new QTimer(); - connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); - timer->start(1000); -} - -int PortStatsModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - if (numPorts.isEmpty()) - return 0; - - if (numPorts.last() == 0) - return 0; - - return (int) e_STAT_MAX; -} - -int PortStatsModel::columnCount(const QModelIndex &parent ) const -{ - if (parent.isValid()) - return 0; - else - if (numPorts.isEmpty()) - return 0; - else - return numPorts.last(); -} - -void PortStatsModel::getDomainIndexes(const QModelIndex &index, - uint &portGroupIdx, uint &portIdx) const -{ - int portNum; - - // TODO(LOW): Optimize using binary search: see qLowerBound() - portNum = index.column() + 1; - for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) - if (portNum <= numPorts.at(portGroupIdx)) - break; - - if (portGroupIdx) - { - if (numPorts.at(portGroupIdx -1)) - portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); - else - portIdx = portNum - 1; - } - else - portIdx = portNum - 1; - - //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); -} - -QVariant PortStatsModel::data(const QModelIndex &index, int role) const -{ - uint pgidx, pidx; - int row; - - // Check for a valid index - if (!index.isValid()) - return QVariant(); - - // Check for row/column limits - row = index.row(); - if (row >= e_STAT_MAX) - return QVariant(); - - if (numPorts.isEmpty()) - return QVariant(); - - if (index.column() >= (numPorts.last())) - return QVariant(); - - getDomainIndexes(index, pgidx, pidx); - - // Check role - if (role == Qt::DisplayRole) - { - OstProto::PortStats stats; - - stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); - - switch(row) - { - // States - case e_LINK_STATE: - return LinkStateName.at(stats.state().link_state()); - - case e_TRANSMIT_STATE: - return BoolStateName.at(stats.state().is_transmit_on()); - - case e_CAPTURE_STATE: - return BoolStateName.at(stats.state().is_capture_on()); - - // Statistics - case e_STAT_FRAMES_RCVD: - return stats.rx_pkts(); - - case e_STAT_FRAMES_SENT: - return stats.tx_pkts(); - - case e_STAT_FRAME_SEND_RATE: - return stats.tx_pps(); - - case e_STAT_FRAME_RECV_RATE: - return stats.rx_pps(); - - case e_STAT_BYTES_RCVD: - return stats.rx_bytes(); - - case e_STAT_BYTES_SENT: - return stats.tx_bytes(); - - case e_STAT_BYTE_SEND_RATE: - return stats.tx_bps(); - - case e_STAT_BYTE_RECV_RATE: - return stats.rx_bps(); - -#if 0 - case e_STAT_FRAMES_RCVD_NIC: - return stats.rx_pkts_nic(); - - case e_STAT_FRAMES_SENT_NIC: - return stats.tx_pkts_nic(); - - case e_STAT_BYTES_RCVD_NIC: - return stats.rx_bytes_nic(); - - case e_STAT_BYTES_SENT_NIC: - return stats.tx_bytes_nic(); -#endif - default: - qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, - index.row()); - return 0; - } - } - else if (role == Qt::TextAlignmentRole) - { - if (row >= e_STATE_START && row <= e_STATE_END) - return Qt::AlignHCenter; - else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) - return Qt::AlignRight; - else - return QVariant(); - } - else - return QVariant(); - -} - -QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ -#ifdef Q_OS_WIN32 - // TODO(MED): The limitations should be the server's not the client's! - // Ideally we shd enhance the protocol to convey limitation(s), if any, - // from server to client - if (role == Qt::ToolTipRole) - { - if (orientation == Qt::Horizontal) - { - return QString("Limitation(s)" - "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" - "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" - "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" - ); - } - else - return QVariant(); - } -#endif - - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) - { - uint portGroupIdx, portIdx; - - getDomainIndexes(index(0, section), portGroupIdx, portIdx); -#ifdef Q_OS_WIN32 - return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); -#else - return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); -#endif - } - else - return PortStatName.at(section); -} - -void PortStatsModel::portListFromIndex(QModelIndexList indices, - QList &portList) -{ - int i, j; - QModelIndexList selectedCols(indices); - - portList.clear(); - - //selectedCols = indices.selectedColumns(); - for (i = 0; i < selectedCols.size(); i++) - { - uint portGroupIdx, portIdx; - - getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); - for (j = 0; j < portList.size(); j++) - { - if (portList[j].portGroupId == portGroupIdx) - break; - } - - if (j >= portList.size()) - { - // PortGroup Not found - PortGroupAndPortList p; - - p.portGroupId = portGroupIdx; - p.portList.append(portIdx); - - portList.append(p); - } - else - { - // PortGroup found - - portList[j].portList.append(portIdx); - } - } -} - -// -// Slots -// -void PortStatsModel::when_portListChanged() -{ - int i, count = 0; - - // recalc numPorts - while (numPorts.size()) - numPorts.removeFirst(); - - for (i = 0; i < pgl->mPortGroups.size(); i++) - { - count += pgl->mPortGroups.at(i)->numPorts(); - numPorts.append(count); - } - - reset(); -} - -void PortStatsModel::on_portStatsUpdate(int port, void*stats) -{ - QModelIndex topLeft = index(port, 0, QModelIndex()); - QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); - - emit dataChanged(topLeft, bottomRight); -} - -void PortStatsModel::updateStats() -{ - // Request each portgroup to fetch updated stats - the port group - // raises a signal once updated stats are available - for (int i = 0; i < pgl->mPortGroups.size(); i++) - pgl->mPortGroups[i]->getPortStats(); -} - -void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) -{ - // FIXME(MED): update only the changed ports, not all - - QModelIndex topLeft = index(0, 0, QModelIndex()); - QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); - - emit dataChanged(topLeft, bottomRight); -} +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + QTimer *timer; + + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return stats.rx_pkts(); + + case e_STAT_FRAMES_SENT: + return stats.tx_pkts(); + + case e_STAT_FRAME_SEND_RATE: + return stats.tx_pps(); + + case e_STAT_FRAME_RECV_RATE: + return stats.rx_pps(); + + case e_STAT_BYTES_RCVD: + return stats.rx_bytes(); + + case e_STAT_BYTES_SENT: + return stats.tx_bytes(); + + case e_STAT_BYTE_SEND_RATE: + return stats.tx_bps(); + + case e_STAT_BYTE_RECV_RATE: + return stats.rx_bps(); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ +#ifdef Q_OS_WIN32 + // TODO(MED): The limitations should be the server's not the client's! + // Ideally we shd enhance the protocol to convey limitation(s), if any, + // from server to client + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + return QString("Limitation(s)" + "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" + "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" + "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" + ); + } + else + return QVariant(); + } +#endif + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); +#ifdef Q_OS_WIN32 + return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); +#else + return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); +#endif + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void*stats) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index e253e22..3baa7c2 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -1,117 +1,117 @@ -#ifndef _PORT_STATS_MODEL_H -#define _PORT_STATS_MODEL_H - -#include -#include - -typedef enum { - // State - e_STATE_START = 0, - - e_LINK_STATE = e_STATE_START, - e_TRANSMIT_STATE, - e_CAPTURE_STATE, - - e_STATE_END = e_CAPTURE_STATE, - - // Statistics - e_STATISTICS_START, - - e_STAT_FRAMES_RCVD = e_STATISTICS_START, - e_STAT_FRAMES_SENT, - e_STAT_FRAME_SEND_RATE, - e_STAT_FRAME_RECV_RATE, - e_STAT_BYTES_RCVD, - e_STAT_BYTES_SENT, - e_STAT_BYTE_SEND_RATE, - e_STAT_BYTE_RECV_RATE, -#if 0 - e_STAT_FRAMES_RCVD_NIC, - e_STAT_FRAMES_SENT_NIC, - e_STAT_BYTES_RCVD_NIC, - e_STAT_BYTES_SENT_NIC, -#endif - - e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, - - e_STAT_MAX -} PortStat; - -static QStringList PortStatName = (QStringList() - << "Link State" - << "Transmit State" - << "Capture State" - - << "Frames Received" - << "Frames Sent" - << "Frame Send Rate (fps)" - << "Frame Receive Rate (fps)" - << "Bytes Received" - << "Bytes Sent" - << "Byte Send Rate (Bps)" - << "Byte Receive Rate (Bps)" -#if 0 - << "Frames Received (NIC)" - << "Frames Sent (NIC)" - << "Bytes Received (NIC)" - << "Bytes Sent (NIC)" -#endif -); - -static QStringList LinkStateName = (QStringList() - << "Unknown" - << "Down" - << "Up" -); - -static QStringList BoolStateName = (QStringList() - << "Off" - << "On" -); - -class PortGroupList; - -class PortStatsModel : public QAbstractTableModel -{ - Q_OBJECT - - public: - - PortStatsModel(PortGroupList *p, 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; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - - class PortGroupAndPortList { - public: - uint portGroupId; - QList portList; - }; - void portListFromIndex(QModelIndexList indices, - QList &portList); - - public slots: - void when_portListChanged(); - void on_portStatsUpdate(int port, void*stats); - void when_portGroup_stats_update(quint32 portGroupId); - - private slots: - void updateStats(); - - private: - PortGroupList *pgl; - - // numPorts stores the num of ports per portgroup - // in the same order as the portgroups are index in the pgl - // Also it stores them as cumulative totals - QList numPorts; - - void getDomainIndexes(const QModelIndex &index, - uint &portGroupIdx, uint &portIdx) const; - -}; - -#endif +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, 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; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index c10834d..9b8b224 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -1,161 +1,161 @@ - -#include "portstatswindow.h" -#include "portstatsmodel.h" -#include "portstatsfilterdialog.h" - -#include "QHeaderView" - -PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - this->pgl = pgl; - model = pgl->getPortStatsModel(); - tvPortStats->setModel(model); - - tvPortStats->verticalHeader()->setHighlightSections(false); - tvPortStats->verticalHeader()->setDefaultSectionSize( - tvPortStats->verticalHeader()->minimumSectionSize()); - -} - -PortStatsWindow::~PortStatsWindow() -{ -} - -/* ------------- SLOTS -------------- */ - -void PortStatsWindow::on_tbStartTransmit_clicked() -{ - QList pgpl; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - startTx(&pgpl[i].portList); - } -} - -void PortStatsWindow::on_tbStopTransmit_clicked() -{ - QList pgpl; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - stopTx(&pgpl[i].portList); - } -} - -void PortStatsWindow::on_tbStartCapture_clicked() -{ - // TODO(MED) - QList pgpl; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - startCapture(&pgpl[i].portList); - } -} - -void PortStatsWindow::on_tbStopCapture_clicked() -{ - // TODO(MED) - QList pgpl; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - stopCapture(&pgpl[i].portList); - } -} - -void PortStatsWindow::on_tbViewCapture_clicked() -{ - // TODO(MED) - QList pgpl; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - pgpl); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < pgpl.size(); i++) - { - pgl->portGroupByIndex(pgpl.at(i).portGroupId). - viewCapture(&pgpl[i].portList); - } -} - -void PortStatsWindow::on_tbClear_clicked() -{ - QList portList; - - // Get selected ports - model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), - portList); - - // Clear selected ports, portgroup by portgroup - for (int i = 0; i < portList.size(); i++) - { - pgl->portGroupByIndex(portList.at(i).portGroupId). - clearPortStats(&portList[i].portList); - } -} - -void PortStatsWindow::on_tbClearAll_clicked() -{ - for (int i = 0; i < pgl->numPortGroups(); i++) - { - pgl->portGroupByIndex(0).clearPortStats(); - } -} - -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) - { - // hide/show sections first ... - for(int i = 0; i < model->columnCount(); i++) - tvPortStats->setColumnHidden(i, !newColumns.contains(i)); - - // ... then for the 'shown' columns, set the visual index - for(int i = 0; i < newColumns.size(); i++) - { - tvPortStats->horizontalHeader()->moveSection(tvPortStats-> - horizontalHeader()->visualIndex(newColumns.at(i)), i); - } - } -} + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h index a390446..b2ad6f2 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -1,37 +1,37 @@ -#ifndef _PORT_STATS_WINDOW_H -#define _PORT_STATS_WINDOW_H - -#include -#include -#include "ui_portstatswindow.h" -#include "portgrouplist.h" -#include "portstatsmodel.h" - -class PortStatsWindow : public QWidget, public Ui::PortStatsWindow -{ - Q_OBJECT - -public: - PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); - ~PortStatsWindow(); - -private: - PortGroupList *pgl; - PortStatsModel *model; - -private slots: - void on_tbStartTransmit_clicked(); - void on_tbStopTransmit_clicked(); - - void on_tbStartCapture_clicked(); - void on_tbStopCapture_clicked(); - void on_tbViewCapture_clicked(); - - void on_tbClear_clicked(); - void on_tbClearAll_clicked(); - - void on_tbFilter_clicked(); -}; - -#endif - +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 23684db..c8dd179 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,416 +1,416 @@ -#include "portswindow.h" -#include "streamlistdelegate.h" -#include "streamconfigdialog.h" -#include -#include - -PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) -{ - StreamListDelegate *delegate = new StreamListDelegate; - //slm = new StreamListModel(); - //plm = new PortGroupList(); - plm = pgl; - - setupUi(this); - - tvStreamList->setItemDelegate(delegate); - - tvStreamList->verticalHeader()->setDefaultSectionSize( - tvStreamList->verticalHeader()->minimumSectionSize()); - - // Populate Context Menu Actions - tvPortList->addAction(actionNew_Port_Group); - tvPortList->addAction(actionDelete_Port_Group); - tvPortList->addAction(actionConnect_Port_Group); - tvPortList->addAction(actionDisconnect_Port_Group); - - tvStreamList->addAction(actionNew_Stream); - tvStreamList->addAction(actionEdit_Stream); - tvStreamList->addAction(actionDelete_Stream); - - tvStreamList->setModel(plm->getStreamModel()); - tvPortList->setModel(plm->getPortModel()); - - connect( plm->getPortModel(), - SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_portModel_dataChanged(const QModelIndex&, - const QModelIndex&))); - - connect(plm->getPortModel(), SIGNAL(modelReset()), - SLOT(when_portModel_reset())); - - connect( tvPortList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_portView_currentChanged(const QModelIndex&, - const QModelIndex&))); - - connect( tvStreamList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_streamView_currentChanged(const QModelIndex&, - const QModelIndex&))); - connect( tvStreamList->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(when_streamView_selectionChanged())); - -#if 0 - connect( tvPortList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); -#endif - - tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); - tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); - - // Initially we don't have any ports/streams - so send signal triggers - when_portView_currentChanged(QModelIndex(), QModelIndex()); - when_streamView_currentChanged(QModelIndex(), QModelIndex()); - - //! \todo Hide the Aggregate Box till we add support - frAggregate->setHidden(true); -} - -PortsWindow::~PortsWindow() -{ -} - -void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) -{ - StreamConfigDialog *scd; - - if (!index.isValid()) - { - qDebug("%s: invalid index", __FUNCTION__); - return; - } - scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), - index.row(), this); - qDebug("stream list activated\n"); - scd->exec(); // TODO: chk retval - delete scd; -} - -void PortsWindow::when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) -{ - plm->getStreamModel()->setCurrentPortIndex(current); - updatePortViewActions(current); - updateStreamViewActions(); - - if (!current.isValid()) - { - qDebug("setting stacked widget to blank page"); - swDetail->setCurrentIndex(2); // blank page - } - else - { - if (plm->isPortGroup(current)) - { - swDetail->setCurrentIndex(1); // portGroup detail page - } - else if (plm->isPort(current)) - { - swDetail->setCurrentIndex(0); // port detail page - } - } -} - -void PortsWindow::when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) -{ - qDebug("stream view current changed"); - updateStreamViewActions(); -} - -void PortsWindow::when_streamView_selectionChanged() -{ - qDebug("stream view selection changed"); - updateStreamViewActions(); -} - -void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, - const QModelIndex& bottomRight) -{ -#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex - if ((tvPortList->currentIndex() >= topLeft) && - (tvPortList->currentIndex() <= bottomRight)) -#endif - if ((topLeft < tvPortList->currentIndex()) || - (topLeft == tvPortList->currentIndex()) && - ((tvPortList->currentIndex() < bottomRight)) || - (tvPortList->currentIndex() == bottomRight)) - { - updatePortViewActions(tvPortList->currentIndex()); - } -} - -void PortsWindow::when_portModel_reset() -{ - when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); -} - -#if 0 -void PortsWindow::updateStreamViewActions(const QModelIndex& current) -{ - if (current.isValid()) - actionDelete_Stream->setEnabled(true); - else - actionDelete_Stream->setDisabled(true); -} -#endif - -void PortsWindow::updateStreamViewActions() -{ - // For some reason hasSelection() returns true even if selection size is 0 - // so additional check for size introduced - if (tvStreamList->selectionModel()->hasSelection() && - (tvStreamList->selectionModel()->selection().size() > 0)) - { - qDebug("Has selection %d", - tvStreamList->selectionModel()->selection().size()); - - // If more than one non-contiguous ranges selected, - // disable "New" and "Edit" - if (tvStreamList->selectionModel()->selection().size() > 1) - { - actionNew_Stream->setDisabled(true); - actionEdit_Stream->setDisabled(true); - } - else - { - actionNew_Stream->setEnabled(true); - - // Enable "Edit" only if the single range has a single row - if (tvStreamList->selectionModel()->selection().at(0).height() > 1) - actionEdit_Stream->setDisabled(true); - else - actionEdit_Stream->setEnabled(true); - } - - // Delete is always enabled as long as we have a selection - actionDelete_Stream->setEnabled(true); - } - else - { - qDebug("No selection"); - actionNew_Stream->setEnabled(true); - actionEdit_Stream->setDisabled(true); - actionDelete_Stream->setDisabled(true); - } -} - -void PortsWindow::updatePortViewActions(const QModelIndex& current) -{ - if (!current.isValid()) - { - qDebug("current is now invalid"); - actionDelete_Port_Group->setDisabled(true); - actionConnect_Port_Group->setDisabled(true); - actionDisconnect_Port_Group->setDisabled(true); - - goto _EXIT; - } - - qDebug("currentChanged %llx", current.internalId()); - - if (plm->isPortGroup(current)) - { - actionDelete_Port_Group->setEnabled(true); - switch(plm->portGroup(current).state()) - { - case QAbstractSocket::UnconnectedState: - case QAbstractSocket::ClosingState: - qDebug("state = unconnected|closing"); - actionConnect_Port_Group->setEnabled(true); - actionDisconnect_Port_Group->setDisabled(true); - break; - - case QAbstractSocket::HostLookupState: - case QAbstractSocket::ConnectingState: - case QAbstractSocket::ConnectedState: - qDebug("state = lookup|connecting|connected"); - actionConnect_Port_Group->setDisabled(true); - actionDisconnect_Port_Group->setEnabled(true); - break; - - - case QAbstractSocket::BoundState: - case QAbstractSocket::ListeningState: - default: - // FIXME(LOW): indicate error - qDebug("unexpected state"); - break; - } - } - else if (plm->isPort(current)) - { - actionDelete_Port_Group->setEnabled(false); - actionConnect_Port_Group->setEnabled(false); - actionDisconnect_Port_Group->setEnabled(false); - } - -_EXIT: - return; -} - -void PortsWindow::on_pbApply_clicked() -{ - QModelIndex curPort; - QModelIndex curPortGroup; - - curPort = tvPortList->selectionModel()->currentIndex(); - if (!curPort.isValid()) - { - qDebug("%s: curPort is invalid", __FUNCTION__); - goto _exit; - } - - if (!plm->isPort(curPort)) - { - qDebug("%s: curPort is not a port", __FUNCTION__); - goto _exit; - } - - curPortGroup = plm->getPortModel()->parent(curPort); - if (!curPortGroup.isValid()) - { - qDebug("%s: curPortGroup is invalid", __FUNCTION__); - goto _exit; - } - if (!plm->isPortGroup(curPortGroup)) - { - qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); - goto _exit; - } - - // FIXME(HI): shd this be a signal? - //portGroup.when_configApply(port); - // FIXME(MED): mixing port id and index!!! - plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); - -_exit: - return; - -#if 0 - // TODO (LOW): This block is for testing only - QModelIndex current = tvPortList->selectionModel()->currentIndex(); - - if (current.isValid()) - qDebug("current = %llx", current.internalId()); - else - qDebug("current is invalid"); -#endif -} - -void PortsWindow::on_actionNew_Port_Group_triggered() -{ - bool ok; - QString text = QInputDialog::getText(this, - "Add Port Group", "Port Group Address (IP[:Port])", - QLineEdit::Normal, lastNewPortGroup, &ok); - - if (ok) - { - QStringList addr = text.split(":"); - if (addr.size() == 1) // Port unspecified - addr.append(QString().setNum(DEFAULT_SERVER_PORT)); - PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); - plm->addPortGroup(*pg); - lastNewPortGroup = text; - } -} - -void PortsWindow::on_actionDelete_Port_Group_triggered() -{ - QModelIndex current = tvPortList->selectionModel()->currentIndex(); - - if (current.isValid()) - plm->removePortGroup(plm->portGroup(current)); -} - -void PortsWindow::on_actionConnect_Port_Group_triggered() -{ - QModelIndex current = tvPortList->selectionModel()->currentIndex(); - - if (current.isValid()) - plm->portGroup(current).connectToHost(); -} - -void PortsWindow::on_actionDisconnect_Port_Group_triggered() -{ - QModelIndex current = tvPortList->selectionModel()->currentIndex(); - - if (current.isValid()) - plm->portGroup(current).disconnectFromHost(); -} -#if 0 -void PortsWindow::on_actionNew_Stream_triggered() -{ - qDebug("New Stream Action"); - - int row = 0; - - if (tvStreamList->currentIndex().isValid()) - row = tvStreamList->currentIndex().row(); - plm->getStreamModel()->insertRows(row, 1); -} - -void PortsWindow::on_actionDelete_Stream_triggered() -{ - qDebug("Delete Stream Action"); - if (tvStreamList->currentIndex().isValid()) - plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); -} -#endif - -void PortsWindow::on_actionNew_Stream_triggered() -{ - qDebug("New Stream Action"); - - // In case nothing is selected, insert 1 row at the top - int row = 0, count = 1; - - // In case we have a single range selected; insert as many rows as - // in the singe selected range before the top of the selected range - if (tvStreamList->selectionModel()->selection().size() == 1) - { - row = tvStreamList->selectionModel()->selection().at(0).top(); - count = tvStreamList->selectionModel()->selection().at(0).height(); - } - - plm->getStreamModel()->insertRows(row, count); -} - -void PortsWindow::on_actionEdit_Stream_triggered() -{ - qDebug("Edit Stream Action"); - - // Ensure we have only one range selected which contains only one row - if ((tvStreamList->selectionModel()->selection().size() == 1) && - (tvStreamList->selectionModel()->selection().at(0).height() == 1)) - { - on_tvStreamList_activated(tvStreamList->selectionModel()-> - selection().at(0).topLeft()); - } -} - -void PortsWindow::on_actionDelete_Stream_triggered() -{ - qDebug("Delete Stream Action"); - - QModelIndex index; - - if (tvStreamList->selectionModel()->hasSelection()) - { - qDebug("SelectedIndexes %d", - tvStreamList->selectionModel()->selectedRows().size()); - while(tvStreamList->selectionModel()->selectedRows().size()) - { - index = tvStreamList->selectionModel()->selectedRows().at(0); - plm->getStreamModel()->removeRows(index.row(), 1); - } - } - else - qDebug("No selection"); -} - - +#include "portswindow.h" +#include "streamlistdelegate.h" +#include "streamconfigdialog.h" +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) +{ + StreamListDelegate *delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect( tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_streamView_currentChanged(const QModelIndex&, + const QModelIndex&))); + connect( tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_streamView_selectionChanged())); + +#if 0 + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); +#endif + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + when_streamView_currentChanged(QModelIndex(), QModelIndex()); + + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); +} + +PortsWindow::~PortsWindow() +{ +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } +} + +void PortsWindow::when_streamView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + qDebug("stream view current changed"); + updateStreamViewActions(); +} + +void PortsWindow::when_streamView_selectionChanged() +{ + qDebug("stream view selection changed"); + updateStreamViewActions(); +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if ((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex()) && + ((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight)) + { + updatePortViewActions(tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +#if 0 +void PortsWindow::updateStreamViewActions(const QModelIndex& current) +{ + if (current.isValid()) + actionDelete_Stream->setEnabled(true); + else + actionDelete_Stream->setDisabled(true); +} +#endif + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + actionNew_Stream->setEnabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setEnabled(false); + actionConnect_Port_Group->setEnabled(false); + actionDisconnect_Port_Group->setEnabled(false); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} +#if 0 +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + int row = 0; + + if (tvStreamList->currentIndex().isValid()) + row = tvStreamList->currentIndex().row(); + plm->getStreamModel()->insertRows(row, 1); +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + if (tvStreamList->currentIndex().isValid()) + plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); +} +#endif + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + + diff --git a/client/portswindow.h b/client/portswindow.h index aec7216..c6db222 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -1,58 +1,58 @@ -#ifndef _PORTS_WINDOW_H -#define _PORTS_WINDOW_H - -#include -#include -#include "ui_portswindow.h" -#include "portgrouplist.h" - -/* TODO -HIGH -MED -LOW -*/ - - -class PortsWindow : public QWidget, private Ui::PortsWindow -{ - Q_OBJECT - - //QAbstractItemModel *slm; // stream list model - PortGroupList *plm; - -public: - PortsWindow(PortGroupList *pgl, QWidget *parent = 0); - ~PortsWindow(); - -private: - QString lastNewPortGroup; - - void updatePortViewActions(const QModelIndex& current); - //void updateStreamViewActions(const QModelIndex& current); - void updateStreamViewActions(); - -private slots: - void on_tvStreamList_activated(const QModelIndex & index); - void when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& previous); - void when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous); - void when_streamView_selectionChanged(); - void when_portModel_dataChanged(const QModelIndex& topLeft, - const QModelIndex& bottomRight); - void when_portModel_reset(); - - void on_pbApply_clicked(); - - void on_actionNew_Port_Group_triggered(); - void on_actionDelete_Port_Group_triggered(); - void on_actionConnect_Port_Group_triggered(); - void on_actionDisconnect_Port_Group_triggered(); - - void on_actionNew_Stream_triggered(); - void on_actionEdit_Stream_triggered(); - void on_actionDelete_Stream_triggered(); -}; - -#endif - +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + + void updatePortViewActions(const QModelIndex& current); + //void updateStreamViewActions(const QModelIndex& current); + void updateStreamViewActions(); + +private slots: + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_streamView_selectionChanged(); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); +}; + +#endif + diff --git a/client/stream.cpp b/client/stream.cpp index 049d554..7d21b14 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -1,60 +1,60 @@ -#include -#include - -#include "stream.h" -//#include "../common/protocollist.h" -#include "../common/protocollistiterator.h" -#include "../common/abstractprotocol.h" - -Stream::Stream() -{ - //mId = 0xFFFFFFFF; - setEnabled(true); -} - -Stream::~Stream() -{ -} - -void Stream::loadProtocolWidgets() -{ -#if 0 - //protocols.loadConfigWidgets(); - foreach(AbstractProtocol* proto, *currentFrameProtocols) - { - proto->loadConfigWidget(); - } -#else - ProtocolListIterator *iter; - - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->loadConfigWidget(); - } - delete iter; -#endif -} - -void Stream::storeProtocolWidgets() -{ -#if 0 - //protocols.storeConfigWidgets(); - foreach(const AbstractProtocol* proto, frameProtocol()) - { - proto->storeConfigWidget(); - _iter->toFront(); - } -#else - ProtocolListIterator *iter; - - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->storeConfigWidget(); - } - delete iter; -#endif -} +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h index 1b4e756..de7508d 100644 --- a/client/stream.h +++ b/client/stream.h @@ -1,23 +1,23 @@ -#ifndef _STREAM_H -#define _STREAM_H - -#include -#include -#include - -#include "../common/protocol.pb.h" -#include "../common/streambase.h" - -class Stream : public StreamBase { - - //quint32 mId; - -public: - Stream(); - ~Stream(); - - void loadProtocolWidgets(); - void storeProtocolWidgets(); -}; - -#endif +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index c6a950d..eb3da50 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -1,925 +1,925 @@ -#include - -#include "streamconfigdialog.h" -#include "stream.h" -#include "abstractprotocol.h" -#include "protocollistiterator.h" - -#include "modeltest.h" - -// FIXME(HI) - remove -#include "../common/protocolmanager.h" -extern ProtocolManager OstProtocolManager; - -int StreamConfigDialog::lastTopLevelTabIndex = 0; - -StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, - QWidget *parent) : QDialog (parent), mPort(port) -{ - OstProto::Stream s; - mCurrentStreamIndex = streamIndex; - mpStream = new Stream; - mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); - mpStream->protoDataCopyFrom(s); - _iter = mpStream->createProtocolListIterator(); - isUpdateInProgress = false; - - setupUi(this); - setupUiExtra(); - - for (int i = ProtoMin; i < ProtoMax; i++) - { - bgProto[i]->setProperty("ProtocolLevel", i); - bgProto[i]->setProperty("ProtocolId", ButtonIdNone); - connect(bgProto[i], SIGNAL(buttonClicked(int)), - this, SLOT(updateProtocol(int))); - } - - //! \todo causes a crash! -#if 0 - connect(lePktLen, SIGNAL(textEdited(QString)), - this, SLOT(updateContents())); -#endif - - // Time to play match the signals and slots! - - // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None - connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); - - // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and - // disable the subsequent protocol group as well - connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); - connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); - connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); - connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); - connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); - connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); - connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); - connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); - - // Setup valid subsequent protocols for L2 and L3 protocols - for (int i = ProtoL2; i <= ProtoL3; i++) - { - foreach(QAbstractButton *btn1, bgProto[i]->buttons()) - { - int id1 = bgProto[i]->id(btn1); - - if (id1 != ButtonIdNone && id1 != ButtonIdOther) - { - int validProtocolCount = 0; - - foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) - { - int id2 = bgProto[i+1]->id(btn2); - - if (id2 != ButtonIdNone && id2 != ButtonIdOther) - { - if (OstProtocolManager.isValidNeighbour(id1, id2)) - { - connect(btn1, SIGNAL(toggled(bool)), - btn2, SLOT(setEnabled(bool))); - validProtocolCount++; - } - else - connect(btn1, SIGNAL(toggled(bool)), - btn2, SLOT(setDisabled(bool))); - } - } - - // If btn1 has no subsequent valid protocols, - // force subsequent Protocol to 'None' - if (validProtocolCount == 0) - connect(btn1, SIGNAL(clicked(bool)), - bgProto[i+1]->button(ButtonIdNone), SLOT(click())); - } - } - } - - mpAvailableProtocolsModel = new QStringListModel( - OstProtocolManager.protocolDatabase(), this); - lvAllProtocols->setModel(mpAvailableProtocolsModel); - mpSelectedProtocolsModel = new QStringListModel(this); - lvSelectedProtocols->setModel(mpSelectedProtocolsModel); - - - connect(lvAllProtocols->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(when_lvAllProtocols_selectionChanged( - const QItemSelection&, const QItemSelection&))); - connect(lvSelectedProtocols->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, - const QModelIndex&))); - - LoadCurrentStream(); - mpPacketModel = new PacketModel(this); - tvPacketTree->setModel(mpPacketModel); - mpPacketModelTester = new ModelTest(mpPacketModel); - tvPacketTree->header()->hide(); - vwPacketDump->setModel(mpPacketModel); - vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); - - // TODO(MED): - - //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP - rbL3Arp->setHidden(true); - rbL3Ipv6->setHidden(true); - rbL4Icmp->setHidden(true); - rbL4Igmp->setHidden(true); - //! \todo Enable navigation of streams - pbPrev->setDisabled(true); - pbNext->setDisabled(true); - //! \todo Support Goto Stream Id - leStreamId->setDisabled(true); - disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); - //! \todo Support Continuous Mode - rbModeContinuous->setDisabled(true); - - // Finally, restore the saved last selected tab for the various tab widgets - twTopLevel->setCurrentIndex(lastTopLevelTabIndex); -} - -void StreamConfigDialog::setupUiExtra() -{ - QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); - QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); - QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); - - // ---- Setup default stuff that cannot be done in designer ---- - bgProto[ProtoL1] = new QButtonGroup(); - bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); - bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); - bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); - - bgProto[ProtoL2] = new QButtonGroup(); -#if 0 - foreach(QRadioButton *btn, gbFrameType->findChildren()) - bgL2Proto->addButton(btn); -#else - bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); - bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); - bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); - bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); - bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); - bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); -#endif - - bgProto[ProtoVlan] = new QButtonGroup(); - bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); - bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); - bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); - - bgProto[ProtoL3] = new QButtonGroup(); -#if 0 - foreach(QRadioButton *btn, gbL3Proto->findChildren()) - bgProto[ProtoL3]->addButton(btn); -#else - bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); - bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); - bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); - bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); - bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); -#endif - - bgProto[ProtoL4] = new QButtonGroup(); -#if 0 - foreach(QRadioButton *btn, gbL4Proto->findChildren()) - bgProto[ProtoL4]->addButton(btn); -#else - bgProto[ProtoL4]->addButton(rbL4None, 0); - bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); - bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); - bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); - bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); - bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); -#endif - - bgProto[ProtoPayload] = new QButtonGroup(); -#if 0 - foreach(QRadioButton *btn, gbPayloadProto->findChildren()) - bgProto[ProtoPayload]->addButton(btn); -#else - bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); - bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); - bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); -#endif - /* - ** Setup Validators - */ - // Meta Data - //! \todo - doesn't seem to work - range validator needs a spinbox? - //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); - - /* - ** Setup Connections - */ - connect(rbSendPackets, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbSendBursts, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbModeFixed, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - connect(rbModeContinuous, SIGNAL(toggled(bool)), - this, SLOT(update_NumPacketsAndNumBursts())); - -} - -StreamConfigDialog::~StreamConfigDialog() -{ - delete mpPacketModelTester; - delete mpPacketModel; - - for (int i = ProtoMin; i < ProtoMax; i++) - delete bgProto[i]; - - delete _iter; - delete mpStream; -} - -void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) -{ - if (mode == "Fixed") - { - lePktLen->setEnabled(true); - lePktLenMin->setDisabled(true); - lePktLenMax->setDisabled(true); - } - else if (mode == "Increment") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else if (mode == "Decrement") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else if (mode == "Random") - { - lePktLen->setDisabled(true); - lePktLenMin->setEnabled(true); - lePktLenMax->setEnabled(true); - } - else - { - qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); - } -} - -void StreamConfigDialog::on_pbPrev_clicked() -{ -#if 0 - StoreCurrentStream(currStreamIdx); - currStreamIdx--; - LoadCurrentStream(currStreamIdx); - - pbPrev->setDisabled((currStreamIdx == 0)); - pbNext->setDisabled((currStreamIdx == 2)); -#endif -} - -void StreamConfigDialog::on_pbNext_clicked() -{ -#if 0 - StoreCurrentStream(currStreamIdx); - currStreamIdx++; - LoadCurrentStream(currStreamIdx); - - pbPrev->setDisabled((currStreamIdx == 0)); - pbNext->setDisabled((currStreamIdx == 2)); -#endif -} - -void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) -{ - qDebug("%s, index = %d", __FUNCTION__, index); - switch (index) - { - case 0: - updateSelectProtocolsSimpleWidget(); - break; - case 1: - updateSelectProtocolsAdvancedWidget(); - break; - default: - qFatal("%s: unexpected index = %d", __FUNCTION__, index); - } -} - -void StreamConfigDialog::when_lvAllProtocols_selectionChanged( - const QItemSelection &selected, const QItemSelection &deselected) -{ - int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); - - qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); - - tbAdd->setEnabled(size > 0); -} - -void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( - const QModelIndex ¤t, const QModelIndex &previous) -{ - qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); - - tbDelete->setEnabled(current.isValid()); - tbUp->setEnabled(current.isValid() && (current.row() != 0)); - tbDown->setEnabled(current.isValid() && - (current.row() != (current.model()->rowCount() - 1))); -} - -void StreamConfigDialog::on_tbAdd_clicked() -{ - int n = 0; - QModelIndex idx2; - AbstractProtocol *p; - QModelIndexList selection; - - selection = lvAllProtocols->selectionModel()->selectedIndexes(); - - // Validation - if (selection.size() == 0) - return; - - idx2 = lvSelectedProtocols->currentIndex(); - if (idx2.isValid()) - n = idx2.row(); - - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; - - p = _iter->next(); - } - - foreach(QModelIndex idx, selection) - _iter->insert(OstProtocolManager.createProtocol( - mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); - - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx2); -} - -void StreamConfigDialog::on_tbDelete_clicked() -{ - int n; - QModelIndex idx; - AbstractProtocol *p; - - idx = lvSelectedProtocols->currentIndex(); - - // Validation - if (!idx.isValid()) - return; - - n = idx.row() + 1; - - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; - - p = _iter->next(); - } - - _iter->remove(); - delete p; - - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx); -} - -void StreamConfigDialog::on_tbUp_clicked() -{ - int m, n; - QModelIndex idx; - AbstractProtocol *p; - - idx = lvSelectedProtocols->currentIndex(); - - // Validation - if (!idx.isValid() || idx.row() == 0) - return; - - m = n = idx.row() + 1; - - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; - - p = _iter->next(); - } - - _iter->remove(); - _iter->previous(); - _iter->insert(p); - - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); -} - -void StreamConfigDialog::on_tbDown_clicked() -{ - int m, n; - QModelIndex idx; - AbstractProtocol *p; - - idx = lvSelectedProtocols->currentIndex(); - - // Validation - if (!idx.isValid() || idx.row() == idx.model()->rowCount()) - return; - - m = n = idx.row() + 1; - - _iter->toFront(); - while (n--) - { - if (!_iter->hasNext()) - return; - - p = _iter->next(); - } - - _iter->remove(); - _iter->next(); - _iter->insert(p); - - updateSelectProtocolsAdvancedWidget(); - lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); -} - -void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() -{ - QStringList selProtoList; - - qDebug("%s", __FUNCTION__); - - _iter->toFront(); - while(_iter->hasNext()) - { - AbstractProtocol* p = _iter->next(); - qDebug("%p -- %d", p, p->protocolNumber()); - selProtoList.append(p->shortName()); - } - mpSelectedProtocolsModel->setStringList(selProtoList); -} - -void StreamConfigDialog::on_twTopLevel_currentChanged(int index) -{ - switch (index) - { - // Protocol Data - case 1: - { - QWidget *selWidget; - - // Hide the ToolBox before modifying it - else we have a crash !!! - tbProtocolData->hide(); - - selWidget = tbProtocolData->currentWidget(); - - // Remove all existing protocol widgets - while (tbProtocolData->count() > 0) - { - QWidget* w = tbProtocolData->widget(0); - tbProtocolData->removeItem(0); - w->setParent(0); - } - - // Repopulate the widgets - _iter->toFront(); - while (_iter->hasNext()) - { - AbstractProtocol* p = _iter->next(); - tbProtocolData->addItem(p->configWidget(), p->name()); - } - - tbProtocolData->setCurrentWidget(selWidget); - - tbProtocolData->show(); - break; - } - - // Packet View - case 3: - { - StoreCurrentStream(); - mpPacketModel->setSelectedProtocols(*_iter); - break; - } - - default: - break; - } -} - -void StreamConfigDialog::update_NumPacketsAndNumBursts() -{ - if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) - leNumPackets->setEnabled(true); - else - leNumPackets->setEnabled(false); - - if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) - leNumBursts->setEnabled(true); - else - leNumBursts->setEnabled(false); -} - -#if 0 -void StreamConfigDialog::on_lePattern_editingFinished() -{ - ulong num = 0; - bool isOk; - QString str; - - num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); - qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); - lePattern->setText(uintToHexStr(num, str, 4)); - qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); -} -#endif - -/*! -Skip protocols upto and including the layer specified. -*/ -bool StreamConfigDialog::skipProtocols(int layer) -{ - _iter->toFront(); - - for (int i = ProtoMin; i <= layer; i++) - { - if(_iter->hasNext()) - { - int id; - QAbstractButton *btn; - - id = _iter->peekNext()->protocolNumber(); - btn = bgProto[i]->button(id); - if (btn) - _iter->next(); - } - } - - return true; -} - -/*! -Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled -*/ -void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) -{ - qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); - foreach(QAbstractButton *btn, protocolGroup->buttons()) - { - int id = protocolGroup->id(btn); - - if ((id != ButtonIdNone) && (id != ButtonIdOther)) - btn->setDisabled(checked); - } -} - -void StreamConfigDialog::forceProtocolNone(bool checked) -{ - QObject *btn; - - btn = sender(); - Q_ASSERT(btn != NULL); - - qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, - checked, btn, rbL1None, rbFtNone, rbL3None); - - if (btn == rbL1None) - { - if (checked) - { - bgProto[ProtoVlan]->button(ButtonIdNone)->click(); - bgProto[ProtoL2]->button(ButtonIdNone)->click(); - bgProto[ProtoPayload]->button(ButtonIdNone)->click(); - } - - disableProtocols(bgProto[ProtoVlan], checked); - disableProtocols(bgProto[ProtoL2], checked); - disableProtocols(bgProto[ProtoPayload], checked); - } - else if (btn == rbFtNone) - { - if (checked) - bgProto[ProtoL3]->button(ButtonIdNone)->click(); - disableProtocols(bgProto[ProtoL3], checked); - } - else if (btn == rbL3None) - { - if (checked) - bgProto[ProtoL4]->button(ButtonIdNone)->click(); - disableProtocols(bgProto[ProtoL4], checked); - } - else - { - Q_ASSERT(1 == 0); // Unreachable code! - } -} - -void StreamConfigDialog::updateProtocol(int newId) -{ - int level; - QButtonGroup *btnGrp; - - btnGrp = static_cast(sender()); - Q_ASSERT(btnGrp != NULL); - - level = btnGrp->property("ProtocolLevel").toInt(); - Q_ASSERT(btnGrp == bgProto[level]); - - __updateProtocol(level, newId); -} - -void StreamConfigDialog::__updateProtocol(int level, int newId) -{ - int oldId; - QButtonGroup *btnGrp; - - Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); - btnGrp = bgProto[level]; - oldId = btnGrp->property("ProtocolId").toInt(); - - qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, - level, oldId, newId, isUpdateInProgress); - - if (newId == oldId) - return; - - if (!isUpdateInProgress) - { - int ret; - AbstractProtocol *p; - - ret = skipProtocols(level-1); - Q_ASSERT(ret == true); - - Q_ASSERT(oldId != newId); - Q_ASSERT(newId != ButtonIdOther); - - switch (oldId) - { - case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( - newId, mpStream)); - break; - - case ButtonIdOther: - default: - Q_ASSERT(_iter->hasNext()); - p =_iter->next(); - - if (newId) - _iter->setValue(OstProtocolManager.createProtocol( - newId, mpStream)); - else - _iter->remove(); - delete p; - if (level == ProtoPayload) - { - while (_iter->hasNext()) - { - p = _iter->next(); - _iter->remove(); - delete p; - } - } - break; - } - } - - btnGrp->setProperty("ProtocolId", newId); - return; -} - -void StreamConfigDialog::updateSelectProtocolsSimpleWidget() -{ - int i; - quint32 id; - QAbstractButton *btn; - - qDebug("%s", __FUNCTION__); - - isUpdateInProgress = true; - - // Reset to default state ... - for (i = ProtoMin; i < ProtoMax; i++) - bgProto[i]->button(ButtonIdNone)->click(); - - // ... now iterate and update - _iter->toFront(); - - for (i = ProtoMin; i < ProtoMax; i++) - { - if (!_iter->hasNext()) - goto _done; - - id = _iter->next()->protocolNumber(); - btn = bgProto[i]->button(id); - - if (btn) - { - if (btn->isEnabled()) - btn->click(); - else - { - btn->setChecked(true); - __updateProtocol(i, id); - } - } - else - { - switch (i) - { - case ProtoVlan: - _iter->previous(); - break; - - case ProtoPayload: - goto _other; - - default: - btn = bgProto[ProtoPayload]->button(id); - if (btn && btn->isEnabled()) - { - btn->click(); - break; - } - else - goto _other; - } - } - } - - // If more protocol(s) beyond payload ... - if (_iter->hasNext()) - { - i = ProtoPayload; - goto _other; - } - - goto _done; - -_other: - for (int j = i; j < ProtoMax; j++) - { - // VLAN doesn't have a "Other" button - if (j == ProtoVlan) - continue; - - bgProto[j]->button(ButtonIdOther)->setChecked(true); - __updateProtocol(j, ButtonIdOther); - } - -_done: - isUpdateInProgress = false; -} - -void StreamConfigDialog::LoadCurrentStream() -{ - QString str; - - qDebug("loading mpStream %p", mpStream); - - // Meta Data - { - cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); - lePktLen->setText(str.setNum(mpStream->frameLen())); - lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); - lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); - } - - // Protocols - { - updateSelectProtocolsSimpleWidget(); - updateSelectProtocolsAdvancedWidget(); - - mpStream->loadProtocolWidgets(); - } - - // Stream Control - { - switch (mpStream->sendUnit()) - { - case Stream::e_su_packets: - rbSendPackets->setChecked(true); - break; - case Stream::e_su_bursts: - rbSendBursts->setChecked(true); - break; - default: - qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); - } - - switch (mpStream->sendMode()) - { - case Stream::e_sm_fixed: - rbModeFixed->setChecked(true); - break; - case Stream::e_sm_continuous: - rbModeContinuous->setChecked(true); - break; - default: - qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); - } - - switch(mpStream->nextWhat()) - { - case Stream::e_nw_stop: - rbActionStop->setChecked(true); - break; - case Stream::e_nw_goto_next: - rbActionGotoNext->setChecked(true); - break; - case Stream::e_nw_goto_id: - rbActionGotoStream->setChecked(true); - break; - default: - qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); - } - - leNumPackets->setText(QString().setNum(mpStream->numPackets())); - leNumBursts->setText(QString().setNum(mpStream->numBursts())); - lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); - lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); - leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); - // TODO(MED): Change this when we support goto to specific stream - leStreamId->setText(QString("0")); - } - qDebug("loading stream done"); -} - -void StreamConfigDialog::StoreCurrentStream() -{ - QString str; - bool isOk; - Stream *pStream = mpStream; - - qDebug("storing pStream %p", pStream); - - // Meta Data - pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); - pStream->setFrameLen(lePktLen->text().toULong(&isOk)); - pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); - pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); - - // Protocols - { - pStream->storeProtocolWidgets(); - } - - // Stream Control - { - if (rbSendPackets->isChecked()) - pStream->setSendUnit(Stream::e_su_packets); - if (rbSendBursts->isChecked()) - pStream->setSendUnit(Stream::e_su_bursts); - - if (rbModeFixed->isChecked()) - pStream->setSendMode(Stream::e_sm_fixed); - if (rbModeContinuous->isChecked()) - pStream->setSendMode(Stream::e_sm_continuous); - - if (rbActionStop->isChecked()) - pStream->setNextWhat(Stream::e_nw_stop); - if (rbActionGotoNext->isChecked()) - pStream->setNextWhat(Stream::e_nw_goto_next); - if (rbActionGotoStream->isChecked()) - pStream->setNextWhat(Stream::e_nw_goto_id); - - pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); - pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); - pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); - pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); - pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); - } -} - -void StreamConfigDialog::on_pbOk_clicked() -{ - OstProto::Stream s; - - // Store dialog contents into stream - StoreCurrentStream(); - - // Copy the data from the "local working copy of stream" to "actual stream" - mpStream->protoDataCopyInto(s); - mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); - - qDebug("stream stored"); - - lastTopLevelTabIndex = twTopLevel->currentIndex(); -} - +#include + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager OstProtocolManager; + +int StreamConfigDialog::lastTopLevelTabIndex = 0; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 and L3 protocols + for (int i = ProtoL2; i <= ProtoL3; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager.isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager.protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); + mpPacketModelTester = new ModelTest(mpPacketModel); + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + + //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP + rbL3Arp->setHidden(true); + rbL3Ipv6->setHidden(true); + rbL4Icmp->setHidden(true); + rbL4Igmp->setHidden(true); + //! \todo Enable navigation of streams + pbPrev->setDisabled(true); + pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); + + // Finally, restore the saved last selected tab for the various tab widgets + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, 0); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + //! \todo - doesn't seem to work - range validator needs a spinbox? + //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager.createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + QWidget *selWidget; + + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + selWidget = tbProtocolData->currentWidget(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + tbProtocolData->setCurrentWidget(selWidget); + + tbProtocolData->show(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager.createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager.createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastTopLevelTabIndex = twTopLevel->currentIndex(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index fcc3f40..0961767 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -1,113 +1,113 @@ -#ifndef _STREAM_CONFIG_DIALOG_H -#define _STREAM_CONFIG_DIALOG_H - -#include -#include "ui_streamconfigdialog.h" -#include "port.h" -#include "stream.h" -#include "packetmodel.h" -#include "modeltest.h" - -#define MAX_MAC_ITER_COUNT 256 -#define MIN_PKT_LEN 64 -#define MAX_PKT_LEN 1522 - -/* -** TODO -** \todo Improve HexStr handling -** -*/ - - -class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog -{ - Q_OBJECT -public: - StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); - ~StreamConfigDialog(); - -private: - - enum ButtonId - { - ButtonIdNone = 0, - ButtonIdOther = -2 - }; - - enum ProtoButtonGroup - { - ProtoMin, - ProtoL1 = 0, - ProtoVlan = 1, - ProtoL2 = 2, - ProtoL3 = 3, - ProtoL4 = 4, - ProtoPayload = 5, - ProtoMax - }; - - QButtonGroup *bgProto[ProtoMax]; - - QStringListModel *mpAvailableProtocolsModel; - QStringListModel *mpSelectedProtocolsModel; - - Port& mPort; - uint mCurrentStreamIndex; - - Stream *mpStream; - ProtocolListIterator *_iter; - - bool isUpdateInProgress; - - PacketModel *mpPacketModel; - ModelTest *mpPacketModelTester; - - // The following static variables are used to track the "selected" tab - // for the various tab widgets so that it can be restored when the dialog - // is opened the next time - static int lastTopLevelTabIndex; - - void setupUiExtra(); - void updateSelectedProtocols(); - void LoadCurrentStream(); - void StoreCurrentStream(); - -private slots: - void on_cmbPktLenMode_currentIndexChanged(QString mode); - void update_NumPacketsAndNumBursts(); - - void on_twTopLevel_currentChanged(int index); - void on_tbSelectProtocols_currentChanged(int index); - - // "Simple" Protocol Selection related - bool skipProtocols(int layer); - - void disableProtocols(QButtonGroup *protocolGroup, bool checked); - void forceProtocolNone(bool checked); - - void updateProtocol(int newId); - void __updateProtocol(int level, int newId); - - void updateSelectProtocolsSimpleWidget(); - - // "Advanced" Protocol Selection related - void when_lvAllProtocols_selectionChanged( - const QItemSelection &selected, const QItemSelection &deselected); - void when_lvSelectedProtocols_currentChanged( - const QModelIndex ¤t, const QModelIndex &previous); - - void on_tbAdd_clicked(); - void on_tbDelete_clicked(); - void on_tbUp_clicked(); - void on_tbDown_clicked(); - - void updateSelectProtocolsAdvancedWidget(); - - void on_pbPrev_clicked(); - void on_pbNext_clicked(); - - void on_pbOk_clicked(); -}; - -#endif - +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 1522 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoPayload = 5, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time + static int lastTopLevelTabIndex; + + void setupUiExtra(); + void updateSelectedProtocols(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 925be4b..c58d25b 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -1,242 +1,242 @@ -#include "stream.h" -#include "streammodel.h" -#include "portgrouplist.h" -#include "qicon.h" - -StreamModel::StreamModel(PortGroupList *p, QObject *parent) - : QAbstractTableModel(parent) -{ - pgl = p; - mCurrentPort = NULL; -} - -int StreamModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - if (mCurrentPort) - return mCurrentPort->numStreams(); - else - return 0; -} - -int StreamModel::columnCount(const QModelIndex &parent ) const -{ - return (int) StreamMaxFields; -} - -Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags flags = QAbstractTableModel::flags(index); - - - switch (index.column()) - { - case StreamIcon: - break; - case StreamName: - flags |= Qt::ItemIsEditable; - break; - case StreamStatus: - flags |= Qt::ItemIsUserCheckable; - break; - case StreamNextWhat: - flags |= Qt::ItemIsEditable; - break; - default: - //qFatal("Missed case in switch!"); - break; - } - - return flags; -} - -QVariant StreamModel::data(const QModelIndex &index, int role) const -{ - // Check for a valid index - if (!index.isValid()) - return QVariant(); - - // Check for row/column limits - if (index.row() >= mCurrentPort->numStreams()) - return QVariant(); - - if (index.column() >= StreamMaxFields) - return QVariant(); - - if (mCurrentPort == NULL) - return QVariant(); - - // Return data based on field and role - switch(index.column()) - { - case StreamIcon: - { - if (role == Qt::DecorationRole) - return QIcon(":/icons/stream_edit.png"); - else - return QVariant(); - break; - } - case StreamName: - { - if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) - return mCurrentPort->streamByIndex(index.row())->name(); - else - return QVariant(); - break; - } - case StreamStatus: - { - if ((role == Qt::CheckStateRole)) - { - if (mCurrentPort->streamByIndex(index.row())->isEnabled()) - return Qt::Checked; - else - return Qt::Unchecked; - } - else - return QVariant(); - break; - } - case StreamNextWhat: - { - int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); - - if (role == Qt::DisplayRole) - return nextWhatOptionList().at(val); - else if (role == Qt::EditRole) - return val; - else - return QVariant(); - - break; - } - default: - qFatal("-------------UNHANDLED STREAM FIELD----------------"); - } - - return QVariant(); -} - -bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (mCurrentPort == NULL) - return false; - - if (index.isValid()) - { - switch (index.column()) - { - // Edit Supported Fields - case StreamName: - mCurrentPort->streamByIndex(index.row())->setName(value.toString()); - emit(dataChanged(index, index)); - return true; - - case StreamStatus: - mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); - emit(dataChanged(index, index)); - return true; - - case StreamNextWhat: - if (role == Qt::EditRole) - { - mCurrentPort->streamByIndex(index.row())->setNextWhat( - (Stream::NextWhat)value.toInt()); - emit(dataChanged(index, index)); - return true; - } - else - return false; - - // Edit Not Supported Fields - case StreamIcon: - return false; - - // Unhandled Stream Field - default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); - break; - } - } - - return false; -} - -QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) - { - switch(section) - { - case StreamIcon: - return QString(""); - break; - case StreamName: - return QString("Name"); - break; - case StreamStatus: - return QString(""); - break; - case StreamNextWhat: - return QString("Goto"); - break; - default: - qDebug("-------------UNHANDLED STREAM FIELD----------------"); - break; - } - } - else - return QString("%1").arg(section+1); - - return QVariant(); -} - -bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) -{ - qDebug("insertRows() row = %d", row); - qDebug("insertRows() count = %d", count); - beginInsertRows(QModelIndex(), row, row+count-1); - for (int i = 0; i < count; i++) - mCurrentPort->newStreamAt(row); - endInsertRows(); - - return true; -} - -bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) -{ - qDebug("removeRows() row = %d", row); - qDebug("removeRows() count = %d", count); - beginRemoveRows(QModelIndex(), row, row+count-1); - for (int i = 0; i < count; i++) - { - mCurrentPort->deleteStreamAt(row); - } - endRemoveRows(); - - return true; -} - -// --------------------- SLOTS ------------------------ - -void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) -{ - if (!current.isValid() || !pgl->isPort(current)) - { - qDebug("current is either invalid or not a port"); - mCurrentPort = NULL; - } - else - { - qDebug("change to valid port"); - quint16 pg = current.internalId() >> 16; - mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; - } - reset(); -} +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &parent ) const +{ + return (int) StreamMaxFields; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + } + reset(); +} diff --git a/client/streammodel.h b/client/streammodel.h index c65b4d9..e73bb18 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -1,57 +1,57 @@ -#ifndef _STREAM_MODEL_H -#define _STREAM_MODEL_H - -#include -#include -#include "port.h" - -class PortGroupList; - -class StreamModel : public QAbstractTableModel -{ - Q_OBJECT - - Port *mCurrentPort; - PortGroupList *pgl; - - public: - StreamModel(PortGroupList *p, QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole); - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - bool insertRows (int row, int count, - const QModelIndex & parent = QModelIndex()); - bool removeRows (int row, int count, - const QModelIndex & parent = QModelIndex()); - -#if 0 // CleanedUp! - // FIXME(HIGH): This *is* like a kludge - QList* currentPortStreamList() - { return &mCurrentPort->mStreams; } -#endif - - public: - enum StreamFields { - StreamIcon = 0, - StreamName, - StreamStatus, - StreamNextWhat, - - StreamMaxFields - }; - - static QStringList nextWhatOptionList() - { return QStringList() << "Stop" << "Next" << "Goto first"; } - - public slots: - void setCurrentPortIndex(const QModelIndex ¤t); - -}; - -#endif +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto index c8bdc05..42caff1 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -1,210 +1,210 @@ -// stream.proto - -package OstProto; - -message StreamId { - required uint32 id = 1; -} - -message StreamCore { - enum FrameLengthMode { - e_fl_fixed = 0; - e_fl_inc = 1; - e_fl_dec = 2; - e_fl_random = 3; - } - - // Basics - optional string name = 1; - optional bool is_enabled = 2; - optional uint32 ordinal = 3; - - // Frame Length (includes CRC) - optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; - optional uint32 frame_len = 15 [default = 64]; - optional uint32 frame_len_min = 16 [default = 64]; - optional uint32 frame_len_max = 17 [default = 1518]; - - // Currently Selected Protocols - //repeated uint32 frame_proto = 20; -} - -message StreamControl { - enum SendUnit { - e_su_packets = 0; - e_su_bursts = 1; - } - - enum SendMode { - e_sm_fixed = 0; - e_sm_continuous = 1; - } - - enum NextWhat { - e_nw_stop = 0; - e_nw_goto_next = 1; - e_nw_goto_id = 2; - } - - optional SendUnit unit = 1 [default = e_su_packets]; - optional SendMode mode = 2 [default = e_sm_fixed]; - optional uint32 num_packets = 3 [default = 1]; - optional uint32 num_bursts = 4 [default = 1]; - optional uint32 packets_per_burst = 5 [default = 10]; - optional NextWhat next = 6 [default = e_nw_goto_next]; - optional uint32 packets_per_sec = 7 [default = 1]; - optional uint32 bursts_per_sec = 8 [default = 1]; -} - -message ProtocolId { - required uint32 id = 1; -} - -message Protocol { - - required ProtocolId protocol_id = 1; - - extensions 51 to 100; // Reserved for Ostinato Use - extensions 101 to 200; // Available for use by protocols - - enum k { - kMacFieldNumber = 51; - kPayloadFieldNumber = 52; - kSampleFieldNumber = 53; - kUserScriptFieldNumber = 54; - - kEth2FieldNumber = 121; - kDot3FieldNumber = 122; - kLlcFieldNumber = 123; - kSnapFieldNumber = 124; - - kSvlanFieldNumber = 125; - kVlanFieldNumber = 126; - - kDot2LlcFieldNumber = 127; - kDot2SnapFieldNumber = 128; - kVlanStackFieldNumber = 129; - - kIp4FieldNumber = 130; - kArpFieldNumber = 131; - - kTcpFieldNumber = 140; - kUdpFieldNumber = 141; - kIcmpFieldNumber = 142; - kIgmpFieldNumber = 143; - } -} - -message Stream { - - required StreamId stream_id = 1; - optional StreamCore core = 2; - optional StreamControl control = 3; - - repeated Protocol protocol = 4; -} - -message Void { - // nothing! -} - -message Ack { - //! \todo (LOW) do we need any fields in 'Ack' -} - -message PortId { - required uint32 id = 1; -} - -message PortIdList { - repeated PortId port_id = 1; -} - -message StreamIdList { - required PortId port_id = 1; - repeated StreamId stream_id = 2; -} - -message Port { - required PortId port_id = 1; - optional string name = 2; - optional string description = 3; - optional bool is_enabled = 4; - optional bool is_exclusive_control = 6; -} - -message PortConfigList { - repeated Port port = 1; -} - -message StreamConfigList { - required PortId port_id = 1; - repeated Stream stream = 2; -} - -message CaptureBuffer { - //! \todo (HIGH) define CaptureBuffer -} - -message CaptureBufferList { - repeated CaptureBuffer list = 1; -} - -enum LinkState { - LinkStateUnknown = 0; - LinkStateDown = 1; - LinkStateUp = 2; -} - -message PortState { - optional LinkState link_state = 1 [default = LinkStateUnknown]; - optional bool is_transmit_on = 2 [default = false]; - optional bool is_capture_on = 3 [default = false]; -} - -message PortStats { - - required PortId port_id = 1; - - optional PortState state = 2; - - optional uint64 rx_pkts = 11; - optional uint64 rx_bytes = 12; - optional uint64 rx_pkts_nic = 13; - optional uint64 rx_bytes_nic = 14; - optional uint64 rx_pps = 15; - optional uint64 rx_bps = 16; - - optional uint64 tx_pkts = 21; - optional uint64 tx_bytes = 22; - optional uint64 tx_pkts_nic = 23; - optional uint64 tx_bytes_nic = 24; - optional uint64 tx_pps = 25; - optional uint64 tx_bps = 26; -} - -message PortStatsList { - repeated PortStats port_stats = 1; -} - -service OstService { - rpc getPortIdList(Void) returns (PortIdList); - rpc getPortConfig(PortIdList) returns (PortConfigList); - - rpc getStreamIdList(PortId) returns (StreamIdList); - rpc getStreamConfig(StreamIdList) returns (StreamConfigList); - rpc addStream(StreamIdList) returns (Ack); - rpc deleteStream(StreamIdList) returns (Ack); - rpc modifyStream(StreamConfigList) returns (Ack); - - rpc startTx(PortIdList) returns (Ack); - rpc stopTx(PortIdList) returns (Ack); - - rpc startCapture(PortIdList) returns (Ack); - rpc stopCapture(PortIdList) returns (Ack); - rpc getCaptureBuffer(PortId) returns (CaptureBuffer); - - rpc getStats(PortIdList) returns (PortStatsList); - rpc clearStats(PortIdList) returns (Ack); -} - +// stream.proto + +package OstProto; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; + + // Currently Selected Protocols + //repeated uint32 frame_proto = 20; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 51 to 100; // Reserved for Ostinato Use + extensions 101 to 200; // Available for use by protocols + + enum k { + kMacFieldNumber = 51; + kPayloadFieldNumber = 52; + kSampleFieldNumber = 53; + kUserScriptFieldNumber = 54; + + kEth2FieldNumber = 121; + kDot3FieldNumber = 122; + kLlcFieldNumber = 123; + kSnapFieldNumber = 124; + + kSvlanFieldNumber = 125; + kVlanFieldNumber = 126; + + kDot2LlcFieldNumber = 127; + kDot2SnapFieldNumber = 128; + kVlanStackFieldNumber = 129; + + kIp4FieldNumber = 130; + kArpFieldNumber = 131; + + kTcpFieldNumber = 140; + kUdpFieldNumber = 141; + kIcmpFieldNumber = 142; + kIgmpFieldNumber = 143; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional bool is_enabled = 4; + optional bool is_exclusive_control = 6; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index ba2b5fe..e87d4ca 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -1,151 +1,151 @@ -#ifndef _PB_HELPER_H -#define _PB_HELPER_H - -#include -#include - -#include - -#if 0 // not reqd. any longer? -class PbHelper -{ -public: - - // FIXME: Change msg from * to & - void ForceSetSingularDefault(::google::protobuf::Message *msg) - { - const ::google::protobuf::Descriptor *desc; - ::google::protobuf::Message::Reflection *refl; - - qDebug("In %s", __FUNCTION__); - - desc = msg->GetDescriptor(); - refl = msg->GetReflection(); - - for (int i=0; i < desc->field_count(); i++) - { - const ::google::protobuf::FieldDescriptor *f; - - f = desc->field(i); - - // Ensure field is singular and not already set - if (f->label() == - ::google::protobuf::FieldDescriptor::LABEL_REPEATED) - continue; - if (refl->HasField(f)) - continue; - - switch(f->type()) - { - case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: - refl->SetDouble(f, refl->GetDouble(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: - refl->SetFloat(f, refl->GetFloat(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_INT32: - case ::google::protobuf::FieldDescriptor::TYPE_SINT32: - case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: - refl->SetInt32(f, refl->GetInt32(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_INT64: - case ::google::protobuf::FieldDescriptor::TYPE_SINT64: - case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: - refl->SetInt64(f, refl->GetInt64(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_UINT32: - case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: - refl->SetUInt32(f, refl->GetUInt32(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_UINT64: - case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: - refl->SetUInt64(f, refl->GetUInt64(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_BOOL: - refl->SetBool(f, refl->GetBool(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_ENUM: - refl->SetEnum(f, refl->GetEnum(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_STRING: - case ::google::protobuf::FieldDescriptor::TYPE_BYTES: - refl->SetString(f, refl->GetString(f)); - break; - - case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: - case ::google::protobuf::FieldDescriptor::TYPE_GROUP: - ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! - break; - - default: - qDebug("unhandled Field Type"); - break; - } - } - } - - bool update( - ::google::protobuf::Message *target, - ::google::protobuf::Message *source) - { - // FIXME(HI): Depracate: use MergeFrom() directly - qDebug("In %s", __FUNCTION__); - target->MergeFrom(*source); - return true; -#if 0 - ::google::protobuf::Message::Reflection *sourceRef; - ::google::protobuf::Message::Reflection *targetRef; - std::vector srcFieldList; - - - if (source->GetDescriptor()->full_name() != - target->GetDescriptor()->full_name()) - goto _error_exit; - - sourceRef = source->GetReflection(); - targetRef = target->GetReflection(); - - sourceRef->ListFields(&srcFieldList); - for (uint i=0; i < srcFieldList.size(); i++) - { - const ::google::protobuf::FieldDescriptor *srcField, *targetField; - - srcField = srcFieldList[i]; - targetField = target->GetDescriptor()->FindFieldByName( - srcField->name()); - - switch(targetField->type()) - { - case ::google::protobuf::FieldDescriptor::TYPE_UINT32: - targetRef->SetUInt32(targetField, - sourceRef->GetUInt32(srcField)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_BOOL: - targetRef->SetBool(targetField, - sourceRef->GetBool(srcField)); - break; - case ::google::protobuf::FieldDescriptor::TYPE_STRING: - targetRef->SetString(targetField, - sourceRef->GetString(srcField)); - break; - default: - qDebug("unhandled Field Type"); - break; - } - } - _error_exit: - qDebug("%s: error!", __FUNCTION__); - return false; -#endif - } -}; -#endif -#endif +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro index 3465da3..bcd5b8d 100644 --- a/rpc/pbrpc.pro +++ b/rpc/pbrpc.pro @@ -1,8 +1,8 @@ -TEMPLATE = lib -CONFIG += qt staticlib -QT += network -DEFINES += HAVE_REMOTE -INCLUDEPATH += "c:\msys\1.0\local\include" -LIBS += -L"C:\msys\1.0\local\lib" -lprotobuf -HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h -SOURCES += rpcserver.cpp pbrpcchannel.cpp +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +INCLUDEPATH += "c:\msys\1.0\local\include" +LIBS += -L"C:\msys\1.0\local\lib" -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 5c3626e..a06d14e 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -1,310 +1,310 @@ -#include "pbrpcchannel.h" - -PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) -{ - isPending = false; - pendingMethodId = -1; // don't care as long as isPending is false - - controller = NULL; - done = NULL; - response = NULL; - - mServerAddress = ip; - mServerPort = port; - mpSocket = new QTcpSocket(this); - - // FIXME: Not quite sure why this ain't working! - // QMetaObject::connectSlotsByName(this); - - connect(mpSocket, SIGNAL(connected()), - this, SLOT(on_mpSocket_connected())); - connect(mpSocket, SIGNAL(disconnected()), - this, SLOT(on_mpSocket_disconnected())); - connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); - connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); - - connect(mpSocket, SIGNAL(readyRead()), - this, SLOT(on_mpSocket_readyRead())); - -} - -PbRpcChannel::~PbRpcChannel() -{ - delete mpSocket; -} - -void PbRpcChannel::establish() -{ - qDebug("In %s", __FUNCTION__); - - mpSocket->connectToHost(mServerAddress, mServerPort); -} - -void PbRpcChannel::establish(QHostAddress ip, quint16 port) -{ - mServerAddress = ip; - mServerPort = port; - establish(); -} - -void PbRpcChannel::tearDown() -{ - qDebug("In %s", __FUNCTION__); - - mpSocket->disconnectFromHost(); -} - -void PbRpcChannel::CallMethod( - const ::google::protobuf::MethodDescriptor *method, - ::google::protobuf::RpcController *controller, - const ::google::protobuf::Message *req, - ::google::protobuf::Message *response, - ::google::protobuf::Closure* done) -{ - char msg[MSGBUF_SIZE]; - int len; - bool ret; - - if (isPending) - { - RpcCall call; - qDebug("RpcChannel: queueing method %d since %d is pending", - method->index(), pendingMethodId); - - call.method = method; - call.controller = controller; - call.request = req; - call.response = response; - call.done = done; - - pendingCallList.append(call); - - Q_ASSERT(pendingCallList.size() < 100); - - return; - } - - if (!req->IsInitialized()) - { - qWarning("RpcChannel: missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); - - qFatal("exiting"); - - controller->SetFailed("Required fields missing"); - done->Run(); - return; - } - - pendingMethodId = method->index(); - this->controller=controller; - this->done=done; - this->response=response; - isPending = true; - - ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); - Q_ASSERT(ret == true); - - len = req->ByteSize(); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type - *((quint16*)(&msg[2])) = HTONS(method->index()); // method id - *((quint32*)(&msg[4])) = HTONL(len); // len - - // Avoid printing stats since it happens every couple of seconds - if (pendingMethodId != 12) - { - qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, - PB_HDR_SIZE + len, req->DebugString().c_str()); - BUFDUMP(msg, PB_HDR_SIZE + len); - } - - mpSocket->write(msg, PB_HDR_SIZE + len); -} - -void PbRpcChannel::on_mpSocket_readyRead() -{ - char msg[MSGBUF_SIZE]; - char *p = (char*)&msg; - int msgLen; - static bool parsing = false; - static quint16 type, method; - static quint32 len; - - //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); - - if (!parsing) - { - // Do we have an entire header? If not, we'll wait ... - if (mpSocket->bytesAvailable() < PB_HDR_SIZE) - { - qDebug("client: not enough data available for a complete header"); - return; - } - - msgLen = mpSocket->read(msg, PB_HDR_SIZE); - - Q_ASSERT(msgLen == PB_HDR_SIZE); - - type = NTOHS(GET16(p+0)); - method = NTOHS(GET16(p+2)); - len = NTOHL(GET32(p+4)); - - //BUFDUMP(msg, PB_HDR_SIZE); - //qDebug("type = %hu, method = %hu, len = %u", type, method, len); - - parsing = true; - } - - switch (type) - { - case PB_MSG_TYPE_BINBLOB: - { - static quint32 cumLen = 0; - QIODevice *blob; - - blob = static_cast(controller)->binaryBlob(); - Q_ASSERT(blob != NULL); - - while ((cumLen < len) && mpSocket->bytesAvailable()) - { - int l; - - l = mpSocket->read(msg, sizeof(msg)); - blob->write(msg, l); - cumLen += l; - } - - qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); - - if (cumLen < len) - return; - - cumLen = 0; - - if (!isPending) - { - qDebug("not waiting for response"); - goto _error_exit; - } - - if (pendingMethodId != method) - { - qDebug("invalid method id %d (expected = %d)", method, - pendingMethodId); - goto _error_exit; - } - - break; - } - - case PB_MSG_TYPE_RESPONSE: - // Wait till we have the entire message - if (mpSocket->bytesAvailable() < len) - { - qDebug("client: not enough data available for a complete msg"); - return; - } - - msgLen = mpSocket->read(msg, sizeof(msg)); - - Q_ASSERT((unsigned) msgLen == len); - - //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); - //BUFDUMP(msg, msgLen); - - if (!isPending) - { - qDebug("not waiting for response"); - goto _error_exit; - } - - if (pendingMethodId != method) - { - qDebug("invalid method id %d (expected = %d)", method, - pendingMethodId); - goto _error_exit; - } - - response->ParseFromArray((void*) msg, len); - - // Avoid printing stats - if (method != 12) - { - qDebug("client(%s): Parsed as %s", __FUNCTION__, - response->DebugString().c_str()); - } - - if (!response->IsInitialized()) - { - qWarning("RpcChannel: missing required fields in response"); - qDebug(response->InitializationErrorString().c_str()); - - controller->SetFailed("Required fields missing"); - } - break; - - default: - qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); - goto _error_exit; - - } - - pendingMethodId = -1; - controller = NULL; - response = NULL; - isPending = false; - parsing = false; - - done->Run(); - - if (pendingCallList.size()) - { - RpcCall call = pendingCallList.takeFirst(); - CallMethod(call.method, call.controller, call.request, call.response, - call.done); - } - - return; - -_error_exit: - parsing = false; - qDebug("client(%s) discarding received msg", __FUNCTION__); - return; -} - -void PbRpcChannel::on_mpSocket_stateChanged( - QAbstractSocket::SocketState socketState) -{ - qDebug("In %s", __FUNCTION__); - emit stateChanged(socketState); -} - -void PbRpcChannel::on_mpSocket_connected() -{ - qDebug("In %s", __FUNCTION__); - emit connected(); -} - -void PbRpcChannel::on_mpSocket_disconnected() -{ - qDebug("In %s", __FUNCTION__); - - pendingMethodId = -1; - controller = NULL; - response = NULL; - isPending = false; - // \todo convert parsing from static to data member - //parsing = false - pendingCallList.clear(); - - emit disconnected(); -} - -void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) -{ - qDebug("In %s", __FUNCTION__); - emit error(socketError); -} - +#include "pbrpcchannel.h" + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msg[MSGBUF_SIZE]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending", + method->index(), pendingMethodId); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); + Q_ASSERT(ret == true); + + len = req->ByteSize(); + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type + *((quint16*)(&msg[2])) = HTONS(method->index()); // method id + *((quint32*)(&msg[4])) = HTONL(len); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 12) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE + len); + } + + mpSocket->write(msg, PB_HDR_SIZE + len); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + char msg[MSGBUF_SIZE]; + char *p = (char*)&msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read(msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = NTOHS(GET16(p+0)); + method = NTOHS(GET16(p+2)); + len = NTOHL(GET32(p+4)); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read(msg, sizeof(msg)); + blob->write(msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + // Wait till we have the entire message + if (mpSocket->bytesAvailable() < len) + { + qDebug("client: not enough data available for a complete msg"); + return; + } + + msgLen = mpSocket->read(msg, sizeof(msg)); + + Q_ASSERT((unsigned) msgLen == len); + + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + response->ParseFromArray((void*) msg, len); + + // Avoid printing stats + if (method != 12) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug(response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + done->Run(); + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h index e467781..8c2efbc 100644 --- a/rpc/pbrpcchannel.h +++ b/rpc/pbrpcchannel.h @@ -1,83 +1,83 @@ -#ifndef _PB_RPC_CHANNEL_H -#define _PB_RPC_CHANNEL_H - -#include -#include - -#include -#include -#include - -#include "pbrpccommon.h" -#include "pbrpccontroller.h" - -class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel -{ - Q_OBJECT - - // If isPending is TRUE, then controller, done, response - // and pendingMethodId correspond to the last method called by - // the service stub - bool isPending; - int pendingMethodId; - - // controller, done, response are set to the corresponding values - // passed by the stub to CallMethod(). They are reset to NULL when - // we get a response back from the server in on_mpSocket_readyRead() - // after calling done->Run(). - - /*! \todo (MED) : change controller, done and response to references - instead of pointers? */ - ::google::protobuf::RpcController *controller; - ::google::protobuf::Closure *done; - ::google::protobuf::Message *response; - - typedef struct _RpcCall { - const ::google::protobuf::MethodDescriptor *method; - ::google::protobuf::RpcController *controller; - const ::google::protobuf::Message *request; - ::google::protobuf::Message *response; - ::google::protobuf::Closure *done; - } RpcCall; - QList pendingCallList; - - QHostAddress mServerAddress; - quint16 mServerPort; - QTcpSocket *mpSocket; - -public: - PbRpcChannel(QHostAddress ip, quint16 port); - ~PbRpcChannel(); - - void establish(); - void establish(QHostAddress ip, quint16 port); - void tearDown(); - - const QHostAddress& serverAddress() const { return mServerAddress; } - quint16 serverPort() const { return mServerPort; } - - QAbstractSocket::SocketState state() const - { return mpSocket->state(); } - - void CallMethod(const ::google::protobuf::MethodDescriptor *method, - ::google::protobuf::RpcController *controller, - const ::google::protobuf::Message *req, - ::google::protobuf::Message *response, - ::google::protobuf::Closure* done); - -signals: - void connected(); - void disconnected(); - void error(QAbstractSocket::SocketError socketError); - void stateChanged(QAbstractSocket::SocketState socketState); - -private slots: - void on_mpSocket_connected(); - void on_mpSocket_disconnected(); - void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); - void on_mpSocket_error(QAbstractSocket::SocketError socketError); - - void on_mpSocket_readyRead(); -}; - -#endif +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index 4ea1281..12040ee 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -1,61 +1,61 @@ -#ifndef _PB_RPC_COMMON_H -#define _PB_RPC_COMMON_H - -//! \todo (LOW) check which one is right - wrong one seems to be working!!!!! -#if 0 -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+0) << 8 ) \ - | (*((quint8*)(p)+1))) -#else -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) -#define GET32(p) (quint32)( \ - (*((quint8*)(p)+3) << 24) \ - | (*((quint8*)(p)+2) << 16) \ - | (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) -#endif - -#define BYTESWAP4(x) \ - (((x & 0xFF000000) >> 24) | \ - ((x & 0x00FF0000) >> 8) | \ - ((x & 0x0000FF00) << 8) | \ - ((x & 0x000000FF) << 24)) - -#define BYTESWAP2(x) \ - (((x & 0xFF00) >> 8) | \ - ((x & 0x00FF) << 8)) - -//! \todo (LOW) : portability -#if 1 -#define HTONL(x) BYTESWAP4(x) -#define NTOHL(x) BYTESWAP4(x) -#define HTONS(x) BYTESWAP2(x) -#define NTOHS(x) BYTESWAP2(x) -#else -#define HTONL(x) (x) -#define NTOHL(x) (x) -#define HTONS(x) (x) -#define NTOHS(x) (x) -#endif - -// Print a HexDump -#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ - (len)).toHex()).toAscii().data()); - -/* -** RPC Header (8) -** - MSG_TYPE (2) -** - METHOD_ID (2) -** - LEN (4) [not including this header] -*/ -#define PB_HDR_SIZE 8 - -#define PB_MSG_TYPE_REQUEST 1 -#define PB_MSG_TYPE_RESPONSE 2 -#define PB_MSG_TYPE_BINBLOB 3 - -#define MSGBUF_SIZE 4096 - -#endif +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +//! \todo (LOW) check which one is right - wrong one seems to be working!!!!! +#if 0 +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+0) << 8 ) \ + | (*((quint8*)(p)+1))) +#else +#define GET16(p) (quint16)( \ + (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) +#define GET32(p) (quint32)( \ + (*((quint8*)(p)+3) << 24) \ + | (*((quint8*)(p)+2) << 16) \ + | (*((quint8*)(p)+1) << 8 ) \ + | (*((quint8*)(p)+0))) +#endif + +#define BYTESWAP4(x) \ + (((x & 0xFF000000) >> 24) | \ + ((x & 0x00FF0000) >> 8) | \ + ((x & 0x0000FF00) << 8) | \ + ((x & 0x000000FF) << 24)) + +#define BYTESWAP2(x) \ + (((x & 0xFF00) >> 8) | \ + ((x & 0x00FF) << 8)) + +//! \todo (LOW) : portability +#if 1 +#define HTONL(x) BYTESWAP4(x) +#define NTOHL(x) BYTESWAP4(x) +#define HTONS(x) BYTESWAP2(x) +#define NTOHS(x) BYTESWAP2(x) +#else +#define HTONL(x) (x) +#define NTOHL(x) (x) +#define HTONS(x) (x) +#define NTOHS(x) (x) +#endif + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#define MSGBUF_SIZE 4096 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index b446c73..99acc5a 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -1,34 +1,34 @@ -#ifndef _PB_RPC_CONTROLLER_H -#define _PB_RPC_CONTROLLER_H - -#include - -class QIODevice; - -class PbRpcController : public ::google::protobuf::RpcController -{ - bool failed; - QIODevice *blob; - std::string errStr; - -public: - PbRpcController() { Reset(); } - - // Client Side Methods - void Reset() { failed=false; blob = NULL; } - bool Failed() const { return failed; } - void StartCancel() { /*! \todo (MED) */} - std::string ErrorText() const { return errStr; } - - // Server Side Methods - void SetFailed(const std::string &reason) - { failed = true; errStr = reason; } - bool IsCanceled() const { return false; }; - void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } - - // srivatsp added - QIODevice* binaryBlob() { return blob; }; - void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; -}; - -#endif +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +class PbRpcController : public ::google::protobuf::RpcController +{ + bool failed; + QIODevice *blob; + std::string errStr; + +public: + PbRpcController() { Reset(); } + + // Client Side Methods + void Reset() { failed=false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 804c4ee..d85c753 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -1,256 +1,256 @@ -//#include "pbhelper.h" -#include "rpcserver.h" - -RpcServer::RpcServer() -{ - server = NULL; - clientSock = NULL; - - service = NULL; - - isPending = false; - pendingMethodId = -1; // don't care as long as isPending is false -} - -RpcServer::~RpcServer() -{ - if (server) - delete server; -} - -bool RpcServer::registerService(::google::protobuf::Service *service, - quint16 tcpPortNum) -{ - this->service = service; - - server = new QTcpServer(); - connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); - if (!server->listen(QHostAddress::Any, tcpPortNum)) - { - qDebug("Unable to start the server: %s", - server->errorString().toAscii().constData()); - errorString_ = QString("Error starting Ostinato server: %1").arg( - server->errorString()); - return false; - } - - qDebug("The server is running on %s: %d", - server->serverAddress().toString().toAscii().constData(), - server->serverPort()); - errorString_ = QString(); - return true; -} - -QString RpcServer::errorString() -{ - return errorString_; -} - -void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) -{ - QIODevice *blob; - char msg[MSGBUF_SIZE]; - int len; - - //qDebug("In RpcServer::done"); - - if (PbRpcController->Failed()) - { - qDebug("rpc failed"); - goto _exit; - } - - blob = PbRpcController->binaryBlob(); - if (blob) - { - len = blob->size(); - qDebug("is binary blob of len %d", len); - - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - (*(quint32*)(&msg[4])) = HTONL(len); // len - - clientSock->write(msg, PB_HDR_SIZE); - - blob->seek(0); - while (!blob->atEnd()) - { - int l; - - len = blob->read(msg, sizeof(msg)); - l = clientSock->write(msg, len); - Q_ASSERT(l == len); - } - - goto _exit; - } - - if (!resp->IsInitialized()) - { - qWarning("response missing required fields!!"); - qDebug(resp->InitializationErrorString().c_str()); - qFatal("exiting"); - goto _exit; - } - - resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); - - len = resp->ByteSize(); - - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - *((quint32*)(&msg[4])) = HTONL(len); // len - - // Avoid printing stats since it happens once every couple of seconds - if (pendingMethodId != 12) - { - qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); - //BUFDUMP(msg, len + 8); - } - - clientSock->write(msg, PB_HDR_SIZE + len); - -_exit: - delete PbRpcController; - isPending = false; -} - -void RpcServer::when_newConnection() -{ - if (clientSock) - { - QTcpSocket *sock; - - qDebug("already connected, no new connections will be accepted"); - - // Accept and close connection - //! \todo (MED) Send reason msg to client - sock = server->nextPendingConnection(); - sock->disconnectFromHost(); - sock->deleteLater(); - goto _exit; - } - - clientSock = server->nextPendingConnection(); - qDebug("accepting new connection from %s: %d", - clientSock->peerAddress().toString().toAscii().constData(), - clientSock->peerPort()); - - connect(clientSock, SIGNAL(readyRead()), - this, SLOT(when_dataAvail())); - connect(clientSock, SIGNAL(disconnected()), - this, SLOT(when_disconnected())); - connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(when_error(QAbstractSocket::SocketError))); - -_exit: - return; -} - -void RpcServer::when_disconnected() -{ - qDebug("connection closed from %s: %d", - clientSock->peerAddress().toString().toAscii().constData(), - clientSock->peerPort()); - - clientSock->deleteLater(); - clientSock = NULL; -} - -void RpcServer::when_error(QAbstractSocket::SocketError socketError) -{ - qDebug("%s", clientSock->errorString().toAscii().constData()); -} - -void RpcServer::when_dataAvail() -{ - char msg[MSGBUF_SIZE]; - int msgLen; - static bool parsing = false; - static quint16 type, method; - static quint32 len; - const ::google::protobuf::MethodDescriptor *methodDesc; - ::google::protobuf::Message *req, *resp; - PbRpcController *controller; - - if (!parsing) - { - if (clientSock->bytesAvailable() < PB_HDR_SIZE) - return; - - msgLen = clientSock->read(msg, PB_HDR_SIZE); - - Q_ASSERT(msgLen == PB_HDR_SIZE); - - type = NTOHS(GET16(&msg[0])); - method = NTOHS(GET16(&msg[2])); - len = NTOHL(GET32(&msg[4])); - //qDebug("type = %d, method = %d, len = %d", type, method, len); - - parsing = true; - } - - if (clientSock->bytesAvailable() < len) - return; - - msgLen = clientSock->read(msg, sizeof(msg)); - Q_ASSERT((unsigned) msgLen == len); - - if (type != PB_MSG_TYPE_REQUEST) - { - qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, - type, PB_MSG_TYPE_REQUEST); - goto _error_exit; - } - - methodDesc = service->GetDescriptor()->method(method); - if (!methodDesc) - { - qDebug("server(%s): invalid method id %d", __FUNCTION__, method); - goto _error_exit; //! \todo Return Error to client - } - - if (isPending) - { - qDebug("server(%s): rpc pending, try again", __FUNCTION__); - goto _error_exit; //! \todo Return Error to client - } - - pendingMethodId = method; - isPending = true; - - req = service->GetRequestPrototype(methodDesc).New(); - resp = service->GetResponsePrototype(methodDesc).New(); - - req->ParseFromArray((void*)msg, len); - if (!req->IsInitialized()) - { - qWarning("Missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); - qFatal("exiting"); - delete req; - delete resp; - - goto _error_exit; - } - //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, - //resp->DebugString().c_str()); - - controller = new PbRpcController; - - //qDebug("before service->callmethod()"); - - service->CallMethod(methodDesc, controller, req, resp, - NewCallback(this, &RpcServer::done, resp, controller)); - - parsing = false; - - return; - -_error_exit: - parsing = false; - qDebug("server(%s): discarding msg from client", __FUNCTION__); - return; -} - +//#include "pbhelper.h" +#include "rpcserver.h" + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) +{ + QIODevice *blob; + char msg[MSGBUF_SIZE]; + int len; + + //qDebug("In RpcServer::done"); + + if (PbRpcController->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = PbRpcController->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + (*(quint32*)(&msg[4])) = HTONL(len); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msg)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!resp->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug(resp->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); + + len = resp->ByteSize(); + + *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type + *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method + *((quint32*)(&msg[4])) = HTONL(len); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 12) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE + len); + +_exit: + delete PbRpcController; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s", clientSock->errorString().toAscii().constData()); +} + +void RpcServer::when_dataAvail() +{ + char msg[MSGBUF_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read(msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = NTOHS(GET16(&msg[0])); + method = NTOHS(GET16(&msg[2])); + len = NTOHL(GET32(&msg[4])); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (clientSock->bytesAvailable() < len) + return; + + msgLen = clientSock->read(msg, sizeof(msg)); + Q_ASSERT((unsigned) msgLen == len); + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + req->ParseFromArray((void*)msg, len); + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug(req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController; + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + NewCallback(this, &RpcServer::done, resp, controller)); + + parsing = false; + + return; + +_error_exit: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index e353628..f4be419 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -1,44 +1,44 @@ -#ifndef _RPC_SERVER_H -#define _RPC_SERVER_H - -#include -#include -#include - -#include -#include - -#include "pbrpccommon.h" -#include "pbrpccontroller.h" - - -class RpcServer : public QObject -{ - Q_OBJECT - - QTcpServer *server; - QTcpSocket *clientSock; - - ::google::protobuf::Service *service; - - bool isPending; - int pendingMethodId; - QString errorString_; - -public: - RpcServer(); //! \todo (LOW) use 'parent' param - virtual ~RpcServer(); - - bool registerService(::google::protobuf::Service *service, - quint16 tcpPortNum); - QString errorString(); - void done(::google::protobuf::Message *resp, PbRpcController *controller); - -private slots: - void when_newConnection(); - void when_disconnected(); - void when_dataAvail(); - void when_error(QAbstractSocket::SocketError socketError); -}; - -#endif +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(::google::protobuf::Message *resp, PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/drone.cpp b/server/drone.cpp index d7609de..9dcccc8 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -1,76 +1,76 @@ -#include "drone.h" - -#include "rpcserver.h" -#include "myservice.h" - -#include -#include - -extern int myport; - -Drone::Drone(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - rpcServer = new RpcServer(); - service = new MyService(); -} - -Drone::~Drone() -{ - trayIcon_->hide(); - delete rpcServer; - delete service; -} - -bool Drone::init() -{ - Q_ASSERT(rpcServer); - - if (!rpcServer->registerService(service, myport ? myport : 7878)) - { - QMessageBox::critical(0, qApp->applicationName(), - rpcServer->errorString()); - return false; - } - - trayIconMenu_ = new QMenu(this); - - trayIconMenu_->addAction(actionShow); - trayIconMenu_->addAction(actionExit); - trayIconMenu_->setDefaultAction(actionShow); - trayIcon_ = new QSystemTrayIcon(); - trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); - trayIcon_->setToolTip(qApp->applicationName()); - trayIcon_->setContextMenu(trayIconMenu_); - trayIcon_->show(); - - connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); - connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), - Qt::QueuedConnection); - - return true; -} - -void Drone::changeEvent(QEvent *event) -{ - if (event->type() == QEvent::WindowStateChange && isMinimized()) - { - emit hideMe(true); - event->ignore(); - return; - } - - QWidget::changeEvent(event); -} - -void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) -{ - if (reason == QSystemTrayIcon::DoubleClick) - { - showNormal(); - activateWindow(); - } -} +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + +extern int myport; + +Drone::Drone(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + trayIcon_->hide(); + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } +} diff --git a/server/drone.h b/server/drone.h index c441af7..c8d9ff2 100644 --- a/server/drone.h +++ b/server/drone.h @@ -1,37 +1,37 @@ -#ifndef _DRONE_H -#define _DRONE_H - -#include "ui_drone.h" - -#include -#include - -class RpcServer; -namespace OstProto { class OstService; } - -class Drone : public QWidget, Ui::Drone -{ - Q_OBJECT - -public: - Drone(QWidget *parent = 0); - ~Drone(); - bool init(); - -signals: - void hideMe(bool hidden); - -protected: - void changeEvent(QEvent *event); - -private: - QSystemTrayIcon *trayIcon_; - QMenu *trayIconMenu_; - RpcServer *rpcServer; - OstProto::OstService *service; - -private slots: - void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - -}; -#endif +#ifndef _DRONE_H +#define _DRONE_H + +#include "ui_drone.h" + +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone +{ + Q_OBJECT + +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +}; +#endif diff --git a/server/drone.pro b/server/drone.pro index a402b4c..e33bb5c 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,26 +1,26 @@ -TEMPLATE = app -CONFIG += qt debug -QT += network script -DEFINES += HAVE_REMOTE WPCAP -INCLUDEPATH += "../rpc" -win32:LIBS += -lwpcap -lpacket -unix:LIBS += -lpcap -win32:LIBS += -L"../common/debug" -lostproto -unix:LIBS += -L"../common" -lostproto -win32:LIBS += -L"../rpc/debug" -lpbrpc -unix:LIBS += -L"../rpc" -lpbrpc -LIBS += -lprotobuf -POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" -RESOURCES += drone.qrc -HEADERS += drone.h -FORMS += drone.ui -SOURCES += \ - drone_main.cpp \ - drone.cpp \ - portmanager.cpp \ - abstractport.cpp \ - pcapport.cpp \ - winpcapport.cpp -SOURCES += myservice.cpp -SOURCES += pcapextra.cpp - +TEMPLATE = app +CONFIG += qt debug +QT += network script +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32:LIBS += -lwpcap -lpacket +unix:LIBS += -lpcap +win32:LIBS += -L"../common/debug" -lostproto +unix:LIBS += -L"../common" -lostproto +win32:LIBS += -L"../rpc/debug" -lpbrpc +unix:LIBS += -L"../rpc" -lpbrpc +LIBS += -lprotobuf +POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +RESOURCES += drone.qrc +HEADERS += drone.h +FORMS += drone.ui +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + diff --git a/server/drone_main.cpp b/server/drone_main.cpp index f6baad2..0bac7c1 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -1,25 +1,25 @@ -#include "drone.h" - -int myport; - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - Drone drone; - - app.setApplicationName(drone.objectName()); - - if (argc > 1) - myport = atoi(argv[1]); - - if (!drone.init()) - exit(-1); - - drone.setWindowFlags(drone.windowFlags() - | Qt::WindowMaximizeButtonHint - | Qt::WindowMinimizeButtonHint); - drone.showMinimized(); - app.exec(); - return 0; -} - +#include "drone.h" + +int myport; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Drone drone; + + app.setApplicationName(drone.objectName()); + + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone.init()) + exit(-1); + + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone.showMinimized(); + app.exec(); + return 0; +} + diff --git a/server/myservice.cpp b/server/myservice.cpp index 4da9b7e..d043df9 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,421 +1,421 @@ - -#include "myservice.h" - -#if 0 -#include -#include -#include "qdebug.h" - -#include "../common/protocollistiterator.h" -#include "../common/abstractprotocol.h" -#endif - -#include "../common/streambase.h" -#include "../rpc/pbrpccontroller.h" -#include "portmanager.h" - -MyService::MyService() -{ - PortManager *portManager = PortManager::instance(); - int n = portManager->portCount(); - - for (int i = 0; i < n; i++) - portInfo.append(portManager->port(i)); -} - -MyService::~MyService() -{ -} - -void MyService::getPortIdList(::google::protobuf::RpcController* controller, - const ::OstProto::Void* request, - ::OstProto::PortIdList* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < portInfo.size(); i++) - { - ::OstProto::PortId *p; - - p = response->add_port_id(); - p->set_id(portInfo[i]->id()); - } - - done->Run(); -} - -void MyService::getPortConfig(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortConfigList* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int id; - - id = request->port_id(i).id(); - if (id < portInfo.size()) - { - OstProto::Port *p; - - p = response->add_port(); - portInfo[id]->protoDataCopyInto(p); - } - } - - done->Run(); -} - -void MyService::getStreamIdList(::google::protobuf::RpcController* controller, - const ::OstProto::PortId* request, - ::OstProto::StreamIdList* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - response->mutable_port_id()->set_id(portId); - for (int i = 0; i < portInfo[portId]->streamCount(); i++) - { - OstProto::StreamId *s; - - s = response->add_stream_id(); - s->set_id(portInfo[portId]->stream(i)->id()); - } - done->Run(); - return; - -_invalid_port: - controller->SetFailed("Invalid Port Id"); - done->Run(); -} - -void MyService::getStreamConfig(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::StreamConfigList* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->port_id().id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - response->mutable_port_id()->set_id(portId); - for (int i = 0; i < request->stream_id_size(); i++) - { - StreamBase *stream; - OstProto::Stream *s; - - stream = portInfo[portId]->stream(request->stream_id(i).id()); - if (!stream) - continue; //! \todo(LOW): Partial status of RPC - - s = response->add_stream(); - stream->protoDataCopyInto(*s); - } - done->Run(); - return; - -_invalid_port: - controller->SetFailed("invalid portid"); - done->Run(); -} - -void MyService::addStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->port_id().id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - for (int i = 0; i < request->stream_id_size(); i++) - { - StreamBase *stream; - - // If stream with same id as in request exists already ==> error!! - stream = portInfo[portId]->stream(request->stream_id(i).id()); - if (stream) - continue; //! \todo (LOW): Partial status of RPC - - // Append a new "default" stream - actual contents of the new stream is - // expected in a subsequent "modifyStream" request - set the stream id - // now itself however!!! - stream = new StreamBase; - stream->setId(request->stream_id(i).id()); - portInfo[portId]->addStream(stream); - - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); - return; - -_invalid_port: - controller->SetFailed("invalid portid"); - done->Run(); -} - -void MyService::deleteStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->port_id().id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - for (int i = 0; i < request->stream_id_size(); i++) - portInfo[portId]->deleteStream(request->stream_id(i).id()); - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); - return; - -_invalid_port: - controller->SetFailed("invalid portid"); - done->Run(); -} - -void MyService::modifyStream(::google::protobuf::RpcController* controller, - const ::OstProto::StreamConfigList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->port_id().id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - for (int i = 0; i < request->stream_size(); i++) - { - StreamBase *stream; - - stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); - if (stream) - { - stream->protoDataCopyFrom(request->stream(i)); - portInfo[portId]->setDirty(); - } - } - - //! \todo(LOW): fill-in response "Ack"???? - - done->Run(); - return; - -_invalid_port: - controller->SetFailed("invalid portid"); - done->Run(); -} - -void MyService::startTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int portId; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo (LOW): partial RPC? - - portInfo[portId]->startTransmit(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); -} - -void MyService::stopTx(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int portId; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo (LOW): partial RPC? - - portInfo[portId]->stopTransmit(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); -} - -void MyService::startCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int portId; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo (LOW): partial RPC? - - portInfo[portId]->startCapture(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); -} - -void MyService::stopCapture(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - for (int i=0; i < request->port_id_size(); i++) - { - int portId; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo (LOW): partial RPC? - - portInfo[portId]->stopCapture(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); -} - -void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, - const ::OstProto::PortId* request, - ::OstProto::CaptureBuffer* response, - ::google::protobuf::Closure* done) -{ - int portId; - - qDebug("In %s", __PRETTY_FUNCTION__); - - portId = request->id(); - if ((portId < 0) || (portId >= portInfo.size())) - goto _invalid_port; - - portInfo[portId]->stopCapture(); - static_cast(controller)->setBinaryBlob( - portInfo[portId]->captureData()); - - done->Run(); - return; - -_invalid_port: - controller->SetFailed("invalid portid"); - done->Run(); -} - -void MyService::getStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::PortStatsList* response, - ::google::protobuf::Closure* done) -{ - //qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int portId; - AbstractPort::PortStats stats; - OstProto::PortStats *s; - OstProto::PortState *st; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo(LOW): partial rpc? - - s = response->add_port_stats(); - s->mutable_port_id()->set_id(request->port_id(i).id()); - - st = s->mutable_state(); - st->set_link_state(portInfo[portId]->linkState()); - st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); - st->set_is_capture_on(portInfo[portId]->isCaptureOn()); - - portInfo[portId]->stats(&stats); - -#if 0 - if (portId == 2) - qDebug(">%llu", stats.rxPkts); -#endif - - s->set_rx_pkts(stats.rxPkts); - s->set_rx_bytes(stats.rxBytes); - s->set_rx_pps(stats.rxPps); - s->set_rx_bps(stats.rxBps); - - s->set_tx_pkts(stats.txPkts); - s->set_tx_bytes(stats.txBytes); - s->set_tx_pps(stats.txPps); - s->set_tx_bps(stats.txBps); - } - - done->Run(); -} - -void MyService::clearStats(::google::protobuf::RpcController* controller, - const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, - ::google::protobuf::Closure* done) -{ - qDebug("In %s", __PRETTY_FUNCTION__); - - for (int i = 0; i < request->port_id_size(); i++) - { - int portId; - - portId = request->port_id(i).id(); - if ((portId < 0) || (portId >= portInfo.size())) - continue; //! \todo (LOW): partial RPC? - - portInfo[portId]->resetStats(); - } - - //! \todo (LOW): fill-in response "Ack"???? - - done->Run(); -} + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ +} + +void MyService::getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->stream(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} From be620e0ab6859481bc1461a46b7a4cd93358089d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 2 Jan 2010 13:49:54 +0000 Subject: [PATCH 039/294] Changes for successful Linux compilation --- client/mainwindow.cpp | 3 ++- client/ostinato.pro | 3 ++- client/portgroup.cpp | 9 +++++++-- common/dot3.h | 2 +- server/drone.pro | 3 ++- server/pcapextra.h | 2 +- server/pcapport.cpp | 7 ++++++- server/pcapport.h | 1 + server/portmanager.cpp | 1 + server/winpcapport.cpp | 4 ++++ server/winpcapport.h | 4 ++++ 11 files changed, 31 insertions(+), 8 deletions(-) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 0f818c5..4b967a6 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -18,7 +18,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) { localServer_ = new QProcess(this); - localServer_->start("drone.exe"); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start("./drone.exe"); pgl = new PortGroupList; diff --git a/client/ostinato.pro b/client/ostinato.pro index 3afbdc2..a644eb9 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -7,7 +7,8 @@ win32:LIBS += -L"../common/debug" -lostproto unix: LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc -POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +win32:POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +unix:POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 4ec94c3..606393c 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -620,13 +620,18 @@ void PortGroup::processStopCaptureAck(OstProto::Ack *ack) void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) { +#ifdef Q_OS_WIN32 + QString viewer("C:/Program Files/Wireshark/wireshark.exe"); +#else + QString viewer("/usr/bin/wireshark"); +#endif + qDebug("In %s", __FUNCTION__); capFile->flush(); capFile->close(); - if (!QProcess::startDetached("C:/Program Files/Wireshark/wireshark.exe", - QStringList() << capFile->fileName())) + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) qDebug("Failed starting Wireshark"); delete buf; diff --git a/common/dot3.h b/common/dot3.h index af045db..925374a 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -4,7 +4,7 @@ #include "abstractprotocol.h" #include "dot3.pb.h" -#include "ui_Dot3.h" +#include "ui_dot3.h" class Dot3ConfigForm : public QWidget, public Ui::dot3 { diff --git a/server/drone.pro b/server/drone.pro index e33bb5c..5fc71d5 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -10,7 +10,8 @@ unix:LIBS += -L"../common" -lostproto win32:LIBS += -L"../rpc/debug" -lpbrpc unix:LIBS += -L"../rpc" -lpbrpc LIBS += -lprotobuf -POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +win32:POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" +unix:POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui diff --git a/server/pcapextra.h b/server/pcapextra.h index 1f1c9f3..de4cec1 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -6,7 +6,7 @@ #ifndef Q_OS_WIN32 -//#define PCAP_OPENFLAG_PROMISCUOUS 1 +#define PCAP_OPENFLAG_PROMISCUOUS 1 struct pcap_send_queue { diff --git a/server/pcapport.cpp b/server/pcapport.cpp index a133812..f709b26 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -22,6 +22,10 @@ PcapPort::PcapPort(int id, const char *device) { if (strcmp(device, dev->name) == 0) { +#ifndef Q_OS_WIN32 + if (dev->name) + data_.set_name(dev->name); +#endif if (dev->description) data_.set_description(dev->description); @@ -214,7 +218,7 @@ void PcapPort::PortTransmitter::setHandle(pcap_t *handle) void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) { - if (usingInternalStats_); + if (usingInternalStats_) delete stats_; stats_ = stats; usingInternalStats_ = false; @@ -384,6 +388,7 @@ void PcapPort::PortCapturer::run() if (stop_) { + qDebug("user requested capture stop\n"); stop_ = false; break; } diff --git a/server/pcapport.h b/server/pcapport.h index a17e665..9f80e57 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -6,6 +6,7 @@ #include #include "abstractport.h" +#include "pcapextra.h" class PcapPort : public AbstractPort { diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 6105559..6442c72 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -2,6 +2,7 @@ #include +#include "pcapport.h" #include "winpcapport.h" PortManager *PortManager::instance_ = NULL; diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 4e64913..b6e536d 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -1,5 +1,7 @@ #include "winpcapport.h" +#ifdef Q_OS_WIN32 + const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; WinPcapPort::WinPcapPort(int id, const char *device) @@ -140,3 +142,5 @@ void WinPcapPort::PortMonitor::run() QThread::msleep(1000); } } + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h index 3f2a3b0..d351fe2 100644 --- a/server/winpcapport.h +++ b/server/winpcapport.h @@ -1,6 +1,8 @@ #ifndef _SERVER_WIN_PCAP_PORT_H #define _SERVER_WIN_PCAP_PORT_H +#ifdef Q_OS_WIN32 + #include "pcapport.h" #include @@ -27,3 +29,5 @@ private: }; #endif + +#endif From 395024cc0c86964babc4b70768ff993afe533f7b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 2 Jan 2010 14:50:19 +0000 Subject: [PATCH 040/294] Fixed Windows compilation issues (introduced while fixing the linux compilation issues!) --- client/portgroup.cpp | 3 ++- server/pcapport.cpp | 2 ++ server/portmanager.cpp | 1 + server/winpcapport.h | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 606393c..75d18a8 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,5 +1,6 @@ -#include +#include #include +#include #include "portgroup.h" diff --git a/server/pcapport.cpp b/server/pcapport.cpp index f709b26..2879680 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -1,5 +1,7 @@ #include "pcapport.h" +#include + pcap_if_t *PcapPort::deviceList_ = NULL; PcapPort::PcapPort(int id, const char *device) diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 6442c72..1877234 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -1,5 +1,6 @@ #include "portmanager.h" +#include #include #include "pcapport.h" diff --git a/server/winpcapport.h b/server/winpcapport.h index d351fe2..80a83c9 100644 --- a/server/winpcapport.h +++ b/server/winpcapport.h @@ -1,6 +1,8 @@ #ifndef _SERVER_WIN_PCAP_PORT_H #define _SERVER_WIN_PCAP_PORT_H +#include + #ifdef Q_OS_WIN32 #include "pcapport.h" From aac2229403503876576a7c2fe00301eccb709791 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 3 Jan 2010 08:56:16 +0000 Subject: [PATCH 041/294] Removed a bunch of gcc4 warnings - mostly "unused param" --- client/dumpview.cpp | 30 +++++++++++++------------- client/hexlineedit.cpp | 2 +- client/packetmodel.cpp | 3 ++- client/packetmodel.h | 10 +++++---- client/portgroup.cpp | 4 ++-- client/portmodel.cpp | 4 ++-- client/portstatsfilterdialog.cpp | 9 ++++---- client/portstatsmodel.cpp | 4 ++-- client/portswindow.cpp | 7 ++++--- client/streamconfigdialog.cpp | 4 ++-- client/streammodel.cpp | 6 +++--- common/abstractprotocol.cpp | 12 +++++------ common/dot3.cpp | 4 ++-- common/llc.cpp | 4 ++-- common/mac.cpp | 4 ++-- common/payload.cpp | 4 ++-- common/sample.cpp | 1 + common/snap.cpp | 4 ++-- common/streambase.cpp | 1 + common/tcp.cpp | 4 ++-- common/udp.cpp | 5 +++-- common/userscript.cpp | 2 +- common/vlan.cpp | 4 ++-- rpc/pbrpccontroller.h | 4 +++- rpc/rpcserver.cpp | 3 ++- server/abstractport.cpp | 2 +- server/myservice.cpp | 36 ++++++++++++++++---------------- server/pcapport.cpp | 4 +++- 28 files changed, 98 insertions(+), 83 deletions(-) diff --git a/client/dumpview.cpp b/client/dumpview.cpp index 0056502..65584ee 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -3,6 +3,7 @@ //! \todo Enable Scrollbars DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) { int w, h; @@ -26,7 +27,7 @@ DumpView::DumpView(QWidget *parent) qDebug("DumpView::DumpView"); } -QModelIndex DumpView::indexAt( const QPoint &point ) const +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const { #if 0 int x = point.x(); @@ -84,12 +85,12 @@ _exit: return QModelIndex(); } -void DumpView::scrollTo( const QModelIndex &index, ScrollHint hint ) +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) { // FIXME: implement scrolling } -QRect DumpView::visualRect( const QModelIndex &index ) const +QRect DumpView::visualRect(const QModelIndex &/*index*/) const { // FIXME: calculate actual rect return rect(); @@ -101,20 +102,20 @@ int DumpView::horizontalOffset() const return horizontalScrollBar()->value(); } -bool DumpView::isIndexHidden( const QModelIndex &index ) const +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const { return false; } -QModelIndex DumpView::moveCursor( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ) +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) { // FIXME(MED): need to implement movement using cursor return currentIndex(); } -void DumpView::setSelection( const QRect &rect, - QItemSelectionModel::SelectionFlags flags ) +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) { // FIXME(HI): calculate indexes using rect selectionModel()->select(QModelIndex(), flags); @@ -125,22 +126,23 @@ int DumpView::verticalOffset() const return verticalScrollBar()->value(); } -QRegion DumpView::visualRegionForSelection( const QItemSelection &selection ) const +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const { // FIXME(HI) return QRegion(rect()); } //protected slots: -void DumpView::dataChanged( const QModelIndex &topLeft, - const QModelIndex &bottomRight ) +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) { // FIXME(HI) update(); } -void DumpView::selectionChanged( const QItemSelection &selected, - const QItemSelection &deselected ) +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) { // FIXME(HI) update(); @@ -220,7 +222,7 @@ void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, } // TODO(LOW): rewrite this function - it's a mess! -void DumpView::paintEvent(QPaintEvent* event) +void DumpView::paintEvent(QPaintEvent* /*event*/) { QStylePainter painter(viewport()); QRect offsetRect = mOffsetPaneTopRect; diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index 4497710..68d7dab 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -9,7 +9,7 @@ HexLineEdit::HexLineEdit( QWidget * parent) //QLineEdit::QLineEdit(parent); } -void HexLineEdit::focusOutEvent( QFocusEvent *e ) +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) { #if 0 const QValidator *v = validator(); diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index df5a30b..11ed59d 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -5,6 +5,7 @@ #include "../common/abstractprotocol.h" PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) { } @@ -51,7 +52,7 @@ int PacketModel::rowCount(const QModelIndex &parent) const return 0; // catch all } -int PacketModel::columnCount(const QModelIndex &parent) const +int PacketModel::columnCount(const QModelIndex &/*parent*/) const { return 1; } diff --git a/client/packetmodel.h b/client/packetmodel.h index cd81a8f..4f3b542 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -16,8 +16,10 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const { return QVariant(); } ; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; QModelIndex parent(const QModelIndex &index) const; @@ -28,8 +30,8 @@ private: struct { quint16 type; -#define ITYP_PROTOCOL 1 -#define ITYP_FIELD 2 +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 quint16 protocol; // protocol is valid for both ITYPs } ws; } IndexId; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 75d18a8..8705224 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -85,7 +85,7 @@ void PortGroup::on_rpcChannel_disconnected() void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { - qDebug("error\n"); + qDebug("%s: error %d", __FUNCTION__, socketError); emit portGroupDataChanged(mPortGroupId); } @@ -429,7 +429,7 @@ _exit: return; } -void PortGroup::processModifyStreamAck(OstProto::Ack *ack) +void PortGroup::processModifyStreamAck(OstProto::Ack */*ack*/) { qDebug("In %s", __FUNCTION__); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 6ab422e..241090d 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -53,7 +53,7 @@ int PortModel::rowCount(const QModelIndex &parent) const } } -int PortModel::columnCount(const QModelIndex &parent ) const +int PortModel::columnCount(const QModelIndex &/*parent*/) const { return 1; // FIXME: hardcoding } @@ -166,7 +166,7 @@ QVariant PortModel::data(const QModelIndex &index, int role) const return QVariant(); } -QVariant PortModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 4291fea..724ee40 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -1,6 +1,7 @@ #include "portstatsfilterdialog.h" PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) { setupUi(this); @@ -92,8 +93,8 @@ void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &ind { QStandardItem *item; - item = mUnselected.takeItem(lvUnselected->currentIndex().row()); - if (mUnselected.removeRow(lvUnselected->currentIndex().row())) + item = mUnselected.takeItem(index.row()); + if (mUnselected.removeRow(index.row())) mSelected.appendRow(item); } @@ -101,8 +102,8 @@ void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index { QStandardItem *item; - item = mSelected.takeItem(lvSelected->currentIndex().row()); - if (mSelected.removeRow(lvSelected->currentIndex().row())) + item = mSelected.takeItem(index.row()); + if (mSelected.removeRow(index.row())) { mUnselected.appendRow(item); mUnselected.sort(0); diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 58993e4..1958d8d 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -261,7 +261,7 @@ void PortStatsModel::when_portListChanged() reset(); } -void PortStatsModel::on_portStatsUpdate(int port, void*stats) +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) { QModelIndex topLeft = index(port, 0, QModelIndex()); QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); @@ -277,7 +277,7 @@ void PortStatsModel::updateStats() pgl->mPortGroups[i]->getPortStats(); } -void PortStatsModel::when_portGroup_stats_update(quint32 portGroupId) +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) { // FIXME(MED): update only the changed ports, not all diff --git a/client/portswindow.cpp b/client/portswindow.cpp index c8dd179..22ae629 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -5,6 +5,7 @@ #include PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) { StreamListDelegate *delegate = new StreamListDelegate; //slm = new StreamListModel(); @@ -90,7 +91,7 @@ void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) } void PortsWindow::when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) + const QModelIndex& /*previous*/) { plm->getStreamModel()->setCurrentPortIndex(current); updatePortViewActions(current); @@ -114,8 +115,8 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& current, } } -void PortsWindow::when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous) +void PortsWindow::when_streamView_currentChanged(const QModelIndex& /*current*/, + const QModelIndex& /*previous*/) { qDebug("stream view current changed"); updateStreamViewActions(); diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index eb3da50..3c75c3f 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -311,7 +311,7 @@ void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) } void StreamConfigDialog::when_lvAllProtocols_selectionChanged( - const QItemSelection &selected, const QItemSelection &deselected) + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); @@ -321,7 +321,7 @@ void StreamConfigDialog::when_lvAllProtocols_selectionChanged( } void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( - const QModelIndex ¤t, const QModelIndex &previous) + const QModelIndex ¤t, const QModelIndex &/*previous*/) { qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); diff --git a/client/streammodel.cpp b/client/streammodel.cpp index c58d25b..76735d0 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -21,7 +21,7 @@ int StreamModel::rowCount(const QModelIndex &parent) const return 0; } -int StreamModel::columnCount(const QModelIndex &parent ) const +int StreamModel::columnCount(const QModelIndex &/*parent*/) const { return (int) StreamMaxFields; } @@ -197,7 +197,7 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r return QVariant(); } -bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) { qDebug("insertRows() row = %d", row); qDebug("insertRows() count = %d", count); @@ -209,7 +209,7 @@ bool StreamModel::insertRows(int row, int count, const QModelIndex &parent) return true; } -bool StreamModel::removeRows(int row, int count, const QModelIndex &parent) +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) { qDebug("removeRows() row = %d", row); qDebug("removeRows() count = %d", count); diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 8773154..f66dfdd 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -36,8 +36,8 @@ AbstractProtocol::~AbstractProtocol() { } -AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream, - AbstractProtocol *parent) +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) { return NULL; } @@ -124,7 +124,7 @@ int AbstractProtocol::frameFieldCount() const return (fieldCount() - metaFieldCount()); } -AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const { return FieldIsNormal; } @@ -187,8 +187,8 @@ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, /*! Sets the value of a field corresponding to index \n Returns true if field is successfully set, false otherwise \n The default implementation always returns false */ -bool AbstractProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) { return false; } @@ -198,7 +198,7 @@ AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const return ProtocolIdNone; } -quint32 AbstractProtocol::protocolId(ProtocolIdType type) const +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const { return 0; } diff --git a/common/dot3.cpp b/common/dot3.cpp index ddf58a8..1c16f40 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -113,8 +113,8 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool Dot3Protocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool Dot3Protocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/llc.cpp b/common/llc.cpp index a4943f7..5a5f1d2 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -130,8 +130,8 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool LlcProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool LlcProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/mac.cpp b/common/mac.cpp index efbd518..30f0ec2 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -233,8 +233,8 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool MacProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/payload.cpp b/common/payload.cpp index 276af95..292c0c5 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -181,8 +181,8 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool PayloadProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/sample.cpp b/common/sample.cpp index 00a65e6..0b7da64 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -220,6 +220,7 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, cksum = protocolFrameCksum(streamIndex, CksumIp); } default: + cksum = 0; // avoid the 'maybe used unitialized' warning break; } diff --git a/common/snap.cpp b/common/snap.cpp index 3eaab91..cfabc1b 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -135,8 +135,8 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool SnapProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool SnapProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/streambase.cpp b/common/streambase.cpp index e40f009..671f4d3 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -191,6 +191,7 @@ quint16 StreamBase::frameLen(int streamIndex) const break; case OstProto::StreamCore::e_fl_random: //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning qsrand(((uint) this)); for (int i = 0; i <= streamIndex; i++) pktLen = qrand(); diff --git a/common/tcp.cpp b/common/tcp.cpp index b6990f5..7a66347 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -374,8 +374,8 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool TcpProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool TcpProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/common/udp.cpp b/common/udp.cpp index e65e0af..b83a07a 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -205,6 +205,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, qDebug("UDP cksum = %hu", cksum); } default: + cksum = 0; break; } @@ -251,8 +252,8 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool UdpProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool UdpProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { //! implement UdpProtocol::setFieldData() return false; diff --git a/common/userscript.cpp b/common/userscript.cpp index 89191fb..ba26c72 100644 --- a/common/userscript.cpp +++ b/common/userscript.cpp @@ -35,7 +35,7 @@ void UserScriptConfigForm::on_programEdit_textChanged() compileButton->setEnabled(true); } -void UserScriptConfigForm::on_compileButton_clicked(bool checked) +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) { protocol_->storeConfigWidget(); if (!protocol_->isScriptValid()) diff --git a/common/vlan.cpp b/common/vlan.cpp index 83ed52d..5abee7d 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -193,8 +193,8 @@ QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool VlanProtocol::setFieldData(int index, const QVariant &value, - FieldAttrib attrib) +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) { return false; } diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 99acc5a..6eef8cd 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -24,7 +24,9 @@ public: void SetFailed(const std::string &reason) { failed = true; errStr = reason; } bool IsCanceled() const { return false; }; - void NotifyOnCancel(::google::protobuf::Closure *callback) { /*! \todo (MED) */ } + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } // srivatsp added QIODevice* binaryBlob() { return blob; }; diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index d85c753..73f6f51 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -160,7 +160,8 @@ void RpcServer::when_disconnected() void RpcServer::when_error(QAbstractSocket::SocketError socketError) { - qDebug("%s", clientSock->errorString().toAscii().constData()); + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); } void RpcServer::when_dataAvail() diff --git a/server/abstractport.cpp b/server/abstractport.cpp index dfaf5b9..7e71e0d 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -8,7 +8,7 @@ AbstractPort::AbstractPort(int id, const char *device) { data_.mutable_port_id()->set_id(id); - data_.set_name(QString("if%1 ").arg(id).toStdString()); + data_.set_name(device); //! \todo (LOW) admin enable/disable of port data_.set_is_enabled(true); diff --git a/server/myservice.cpp b/server/myservice.cpp index d043df9..123e9ce 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -27,8 +27,8 @@ MyService::~MyService() { } -void MyService::getPortIdList(::google::protobuf::RpcController* controller, - const ::OstProto::Void* request, +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, ::OstProto::PortIdList* response, ::google::protobuf::Closure* done) { @@ -45,7 +45,7 @@ void MyService::getPortIdList(::google::protobuf::RpcController* controller, done->Run(); } -void MyService::getPortConfig(::google::protobuf::RpcController* controller, +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done) @@ -134,7 +134,7 @@ _invalid_port: void MyService::addStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; @@ -175,7 +175,7 @@ _invalid_port: void MyService::deleteStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; @@ -201,7 +201,7 @@ _invalid_port: void MyService::modifyStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamConfigList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; @@ -234,9 +234,9 @@ _invalid_port: done->Run(); } -void MyService::startTx(::google::protobuf::RpcController* controller, +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); @@ -257,9 +257,9 @@ void MyService::startTx(::google::protobuf::RpcController* controller, done->Run(); } -void MyService::stopTx(::google::protobuf::RpcController* controller, +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); @@ -280,9 +280,9 @@ void MyService::stopTx(::google::protobuf::RpcController* controller, done->Run(); } -void MyService::startCapture(::google::protobuf::RpcController* controller, +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); @@ -303,9 +303,9 @@ void MyService::startCapture(::google::protobuf::RpcController* controller, done->Run(); } -void MyService::stopCapture(::google::protobuf::RpcController* controller, +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); @@ -327,7 +327,7 @@ void MyService::stopCapture(::google::protobuf::RpcController* controller, void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, - ::OstProto::CaptureBuffer* response, + ::OstProto::CaptureBuffer* /*response*/, ::google::protobuf::Closure* done) { int portId; @@ -350,7 +350,7 @@ _invalid_port: done->Run(); } -void MyService::getStats(::google::protobuf::RpcController* controller, +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::PortStatsList* response, ::google::protobuf::Closure* done) @@ -397,9 +397,9 @@ void MyService::getStats(::google::protobuf::RpcController* controller, done->Run(); } -void MyService::clearStats(::google::protobuf::RpcController* controller, +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, - ::OstProto::Ack* response, + ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 2879680..9d1e4fb 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -24,7 +24,9 @@ PcapPort::PcapPort(int id, const char *device) { if (strcmp(device, dev->name) == 0) { -#ifndef Q_OS_WIN32 +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else if (dev->name) data_.set_name(dev->name); #endif From 984d65b27d9b3d703152390f1dd042ad7a7f78a7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 3 Jan 2010 13:57:47 +0000 Subject: [PATCH 042/294] - PortTransmitter on Windows now uses the Win32 QueryPerformanceCounter() instead of QThread::usleep() for more accurate timing - Port Stats limitations are now sent from the server to the client (as it should have always been!) --- client/port.h | 2 ++ client/portstatsmodel.cpp | 23 ++++++++------------ common/protocol.proto | 36 +++++++++++++++----------------- server/pcapport.cpp | 44 ++++++++++++++++++++++++++++++++++++++- server/pcapport.h | 2 ++ 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/client/port.h b/client/port.h index 53842a5..06162a0 100644 --- a/client/port.h +++ b/client/port.h @@ -45,6 +45,8 @@ public: { return QString().fromStdString(d.name()); } const QString description() const { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } AdminStatus adminStatus() { return (d.is_enabled()?AdminEnable:AdminDisable); } ControlMode controlMode() diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 1958d8d..72b3d16 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -165,24 +165,23 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const { -#ifdef Q_OS_WIN32 - // TODO(MED): The limitations should be the server's not the client's! - // Ideally we shd enhance the protocol to convey limitation(s), if any, - // from server to client if (role == Qt::ToolTipRole) { if (orientation == Qt::Horizontal) { - return QString("Limitation(s)" - "

Frames/Bytes Receieved: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
" - "Frames/Bytes Sent: Only Ostinato Tx pkts (Tx by others NOT included)

" - "

Rx/Tx Rates are derived from the above and hence subject to same limitations

" - ); + QString notes; + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); } else return QVariant(); } -#endif if (role != Qt::DisplayRole) return QVariant(); @@ -192,11 +191,7 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in uint portGroupIdx, portIdx; getDomainIndexes(index(0, section), portGroupIdx, portIdx); -#ifdef Q_OS_WIN32 - return QString("Port %1-%2 (*)").arg(portGroupIdx).arg(portIdx); -#else return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); -#endif } else return PortStatName.at(section); diff --git a/common/protocol.proto b/common/protocol.proto index 42caff1..86ceb59 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -15,18 +15,15 @@ message StreamCore { } // Basics - optional string name = 1; - optional bool is_enabled = 2; - optional uint32 ordinal = 3; + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; // Frame Length (includes CRC) - optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; - optional uint32 frame_len = 15 [default = 64]; - optional uint32 frame_len_min = 16 [default = 64]; - optional uint32 frame_len_max = 17 [default = 1518]; - - // Currently Selected Protocols - //repeated uint32 frame_proto = 20; + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; } message StreamControl { @@ -46,14 +43,14 @@ message StreamControl { e_nw_goto_id = 2; } - optional SendUnit unit = 1 [default = e_su_packets]; - optional SendMode mode = 2 [default = e_sm_fixed]; - optional uint32 num_packets = 3 [default = 1]; - optional uint32 num_bursts = 4 [default = 1]; - optional uint32 packets_per_burst = 5 [default = 10]; - optional NextWhat next = 6 [default = e_nw_goto_next]; - optional uint32 packets_per_sec = 7 [default = 1]; - optional uint32 bursts_per_sec = 8 [default = 1]; + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; } message ProtocolId { @@ -129,7 +126,8 @@ message Port { required PortId port_id = 1; optional string name = 2; optional string description = 3; - optional bool is_enabled = 4; + optional string notes = 4; + optional bool is_enabled = 5; optional bool is_exclusive_control = 6; } diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 9d1e4fb..ced12ab 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -2,6 +2,10 @@ #include +#ifdef Q_OS_WIN32 +#include +#endif + pcap_if_t *PcapPort::deviceList_ = NULL; PcapPort::PcapPort(int id, const char *device) @@ -40,11 +44,25 @@ PcapPort::PcapPort(int id, const char *device) void PcapPort::init() { + QString notes; + + if (!monitorRx_->isDirectional() && !data_.is_exclusive_control()) + notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
"); + if (!monitorTx_->isDirectional()) + { transmitter_->useExternalStats(&stats_); + notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
"); + } transmitter_->setHandle(monitorRx_->handle()); + if (!notes.isEmpty()) + data_.set_notes(QString("Limitation(s)" + "

%1
" + "Rx/Tx Rates are also subject to above limitation(s)

"). + arg(notes).toStdString()); + monitorRx_->start(); monitorTx_->start(); } @@ -150,6 +168,14 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) { char errbuf[PCAP_ERRBUF_SIZE]; +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif returnToQIdx_ = -1; stop_ = false; stats_ = new AbstractPort::PortStats; @@ -316,13 +342,29 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, if (usec) { - QThread::usleep(usec); + udelay(usec); ts = hdr->ts; } } } } +void PcapPort::PortTransmitter::udelay(long usec) +{ +#ifdef Q_OS_WIN32 + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#else + QThread::usleep(usec); +#endif +} + PcapPort::PortCapturer::PortCapturer(const char *device) { device_ = QString::fromAscii(device); diff --git a/server/pcapport.h b/server/pcapport.h index 9f80e57..28e9499 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -80,8 +80,10 @@ protected: void run(); void stop(); private: + void udelay(long usec); int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + quint64 ticksFreq_; QList sendQueueList_; int returnToQIdx_; bool usingInternalStats_; From c97ae3bc55f7667b19af10c0fb80ced288166c82 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 8 Jan 2010 15:18:55 +0000 Subject: [PATCH 043/294] Fixed a bunch of memory leaks - thanks to the superb Valgrind! --- client/port.cpp | 2 ++ client/port.h | 3 ++- client/portgroup.cpp | 6 ++++-- client/portgrouplist.cpp | 16 +++++++++++++--- client/portgrouplist.h | 10 +++++++--- client/portstatsmodel.cpp | 8 ++++++-- client/portstatsmodel.h | 5 +++++ client/portswindow.cpp | 9 ++++++--- client/portswindow.h | 2 ++ common/protocolmanager.cpp | 10 ++++++++++ common/protocolmanager.h | 1 + common/streambase.cpp | 6 ++++-- rpc/rpcserver.cpp | 34 ++++++++++++++++++---------------- rpc/rpcserver.h | 14 ++++++++------ server/drone.cpp | 3 +++ server/pcapport.cpp | 6 ++++++ server/pcapport.h | 1 + server/portmanager.cpp | 2 ++ server/winpcapport.cpp | 2 ++ 19 files changed, 102 insertions(+), 38 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 2509927..fd091f4 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -22,6 +22,8 @@ Port::Port(quint32 id, quint32 portGroupId) Port::~Port() { + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); } void Port::updatePortConfig(OstProto::Port *port) diff --git a/client/port.h b/client/port.h index 06162a0..2c36660 100644 --- a/client/port.h +++ b/client/port.h @@ -13,8 +13,9 @@ class Port : public QObject { Q_OBJECT static uint mAllocStreamId; + OstProto::Port d; - OstProto::PortStats stats; + OstProto::PortStats stats; // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' quint32 mPortId; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 8705224..ba5364c 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -23,8 +23,7 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) rpcController = new PbRpcController; rpcControllerStats = new PbRpcController; isGetStatsPending_ = false; - serviceStub = new OstProto::OstService::Stub(rpcChannel, - OstProto::OstService::STUB_OWNS_CHANNEL); + serviceStub = new OstProto::OstService::Stub(rpcChannel); // FIXME(LOW):Can't for my life figure out why this ain't working! //QMetaObject::connectSlotsByName(this); @@ -44,6 +43,9 @@ PortGroup::~PortGroup() // Disconnect and free rpc channel etc. PortGroup::disconnectFromHost(); delete serviceStub; + delete rpcControllerStats; + delete rpcController; + delete rpcChannel; } diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 49aa539..b76fb15 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -11,15 +11,25 @@ PortGroupList::PortGroupList() PortGroup *pg; // TODO(LOW): Remove - new ModelTest(getStreamModel()); - new ModelTest(getPortModel()); - new ModelTest(getPortStatsModel()); + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); // Add the "Local" Port Group pg = new PortGroup; addPortGroup(*pg); } +PortGroupList::~PortGroupList() +{ + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; +} + bool PortGroupList::isPortGroup(const QModelIndex& index) { return mPortGroupListModel.isPortGroup(index); diff --git a/client/portgrouplist.h b/client/portgrouplist.h index 3dd8f5f..e8d2433 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -21,13 +21,17 @@ class PortGroupList : public QObject { QList mPortGroups; PortModel mPortGroupListModel; - StreamModel mStreamListModel; - PortStatsModel mPortStatsModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; // Methods public: PortGroupList(); - + ~PortGroupList(); PortModel* getPortModel() { return &mPortGroupListModel; } PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 72b3d16..cc76c1f 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -6,8 +6,6 @@ PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) : QAbstractTableModel(parent) { - QTimer *timer; - pgl = p; timer = new QTimer(); @@ -15,6 +13,12 @@ PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) timer->start(1000); } +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + int PortStatsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 3baa7c2..0a2e3aa 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -4,6 +4,8 @@ #include #include +class QTimer; + typedef enum { // State e_STATE_START = 0, @@ -78,6 +80,7 @@ class PortStatsModel : public QAbstractTableModel public: PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; @@ -109,6 +112,8 @@ class PortStatsModel : public QAbstractTableModel // Also it stores them as cumulative totals QList numPorts; + QTimer *timer; + void getDomainIndexes(const QModelIndex &index, uint &portGroupIdx, uint &portIdx) const; diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 22ae629..505891f 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,13 +1,15 @@ #include "portswindow.h" -#include "streamlistdelegate.h" -#include "streamconfigdialog.h" + #include #include +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent) { - StreamListDelegate *delegate = new StreamListDelegate; + delegate = new StreamListDelegate; //slm = new StreamListModel(); //plm = new PortGroupList(); plm = pgl; @@ -72,6 +74,7 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) PortsWindow::~PortsWindow() { + delete delegate; } void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) diff --git a/client/portswindow.h b/client/portswindow.h index c6db222..e327c49 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -12,6 +12,7 @@ MED LOW */ +class QAbstractItemDelegate; class PortsWindow : public QWidget, private Ui::PortsWindow { @@ -26,6 +27,7 @@ public: private: QString lastNewPortGroup; + QAbstractItemDelegate *delegate; void updatePortViewActions(const QModelIndex& current); //void updateStreamViewActions(const QModelIndex& current); diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index eb948ba..faaef8f 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -62,6 +62,16 @@ ProtocolManager::ProtocolManager() populateNeighbourProtocols(); } +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + while (!protocolList.isEmpty()) + delete protocolList.takeFirst(); +} + void ProtocolManager::registerProtocol(int protoNumber, void *protoInstanceCreator) { diff --git a/common/protocolmanager.h b/common/protocolmanager.h index 86ccf5d..c80315e 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -19,6 +19,7 @@ class ProtocolManager public: ProtocolManager(); + ~ProtocolManager(); void registerProtocol(int protoNumber, void *protoInstanceCreator); diff --git a/common/streambase.cpp b/common/streambase.cpp index 671f4d3..a7320ee 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -50,9 +50,11 @@ StreamBase::StreamBase() : StreamBase::~StreamBase() { - delete mStreamId; - delete mCore; + currentFrameProtocols->destroy(); + delete currentFrameProtocols; delete mControl; + delete mCore; + delete mStreamId; } void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 73f6f51..c989946 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -10,6 +10,7 @@ RpcServer::RpcServer() isPending = false; pendingMethodId = -1; // don't care as long as isPending is false + controller_.Reset(); } RpcServer::~RpcServer() @@ -46,21 +47,22 @@ QString RpcServer::errorString() return errorString_; } -void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcController) +void RpcServer::done(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { - QIODevice *blob; - char msg[MSGBUF_SIZE]; - int len; + QIODevice *blob; + char msg[MSGBUF_SIZE]; + int len; //qDebug("In RpcServer::done"); - if (PbRpcController->Failed()) + if (controller_.Failed()) { qDebug("rpc failed"); goto _exit; } - blob = PbRpcController->binaryBlob(); + blob = controller_.binaryBlob(); if (blob) { len = blob->size(); @@ -85,17 +87,17 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo goto _exit; } - if (!resp->IsInitialized()) + if (!response->IsInitialized()) { qWarning("response missing required fields!!"); - qDebug(resp->InitializationErrorString().c_str()); + qDebug(response->InitializationErrorString().c_str()); qFatal("exiting"); goto _exit; } - resp->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); + response->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); - len = resp->ByteSize(); + len = response->ByteSize(); *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method @@ -105,14 +107,15 @@ void RpcServer::done(::google::protobuf::Message *resp, PbRpcController *PbRpcCo if (pendingMethodId != 12) { qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + PB_HDR_SIZE, resp->DebugString().c_str()); + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); //BUFDUMP(msg, len + 8); } clientSock->write(msg, PB_HDR_SIZE + len); _exit: - delete PbRpcController; + delete request; + delete response; isPending = false; } @@ -173,7 +176,6 @@ void RpcServer::when_dataAvail() static quint32 len; const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; - PbRpcController *controller; if (!parsing) { @@ -238,12 +240,12 @@ void RpcServer::when_dataAvail() //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, //resp->DebugString().c_str()); - controller = new PbRpcController; + controller_.Reset(); //qDebug("before service->callmethod()"); - service->CallMethod(methodDesc, controller, req, resp, - NewCallback(this, &RpcServer::done, resp, controller)); + service->CallMethod(methodDesc, &controller_, req, resp, + NewCallback(this, &RpcServer::done, req, resp)); parsing = false; diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index f4be419..9b8cd23 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -16,13 +16,14 @@ class RpcServer : public QObject { Q_OBJECT - QTcpServer *server; - QTcpSocket *clientSock; + QTcpServer *server; + QTcpSocket *clientSock; - ::google::protobuf::Service *service; + ::google::protobuf::Service *service; - bool isPending; - int pendingMethodId; + bool isPending; + int pendingMethodId; + PbRpcController controller_; QString errorString_; public: @@ -32,7 +33,8 @@ public: bool registerService(::google::protobuf::Service *service, quint16 tcpPortNum); QString errorString(); - void done(::google::protobuf::Message *resp, PbRpcController *controller); + void done(::google::protobuf::Message *req, + ::google::protobuf::Message *resp); private slots: void when_newConnection(); diff --git a/server/drone.cpp b/server/drone.cpp index 9dcccc8..0e03b29 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -20,6 +20,9 @@ Drone::Drone(QWidget *parent) Drone::~Drone() { trayIcon_->hide(); + + delete trayIcon_; + delete trayIconMenu_; delete rpcServer; delete service; } diff --git a/server/pcapport.cpp b/server/pcapport.cpp index ced12ab..ce576f2 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -195,6 +195,12 @@ _open_error: usingInternalHandle_ = false; } +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; +} + void PcapPort::PortTransmitter::clearPacketList() { Q_ASSERT(!isRunning()); diff --git a/server/pcapport.h b/server/pcapport.h index 28e9499..423d15f 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -69,6 +69,7 @@ protected: { public: PortTransmitter(const char *device); + ~PortTransmitter(); void clearPacketList(); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 1877234..bec541f 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -45,6 +45,8 @@ PortManager::PortManager() PortManager::~PortManager() { + while (!portList_.isEmpty()) + delete portList_.takeFirst(); } PortManager* PortManager::instance() diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index b6e536d..8afefba 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -24,6 +24,8 @@ WinPcapPort::WinPcapPort(int id, const char *device) WinPcapPort::~WinPcapPort() { + delete monitorRx_; + delete monitorTx_; } OstProto::LinkState WinPcapPort::linkState() From 78a2de040b7e2e1fe7c993d67644f49b9060e4d0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 10 Jan 2010 06:21:39 +0000 Subject: [PATCH 044/294] Enhancements - (Abs/Pcap/WinPcap)Port: Inter SendQueue Timing implemented using a dummy packet length of zero (this is not supported by the winpcap pcap_sendqueue_transmit() but since we don't use that API we are not affected) - (Abs/Pcap/WinPcap)Port: Loop Mode Timing across loop iterations implemented - Added the year 2010 to Copyright notice in the About dialog Fixes - (Win)PcapPort: Fixed the wrong rates when packets sent in 'burst' mode --- client/about.ui | 2 +- server/abstractport.cpp | 26 +++++++++++--------------- server/abstractport.h | 2 +- server/pcapport.cpp | 39 ++++++++++++++++++++++----------------- server/pcapport.h | 10 ++++++---- 5 files changed, 41 insertions(+), 38 deletions(-) diff --git a/client/about.ui b/client/about.ui index c7f914c..6ce0aed 100644 --- a/client/about.ui +++ b/client/about.ui @@ -51,7 +51,7 @@ p, li { white-space: pre-wrap; } - Copyright (c) 2007-2009 Srivats P. + Copyright (c) 2007-2010 Srivats P. Qt::AlignCenter diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 7e71e0d..de2cecc 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -73,22 +73,18 @@ void AbstractPort::updatePacketList() int len; bool isVariable; uchar pktBuf[2000]; - long sec; - long usec; + long sec = 0; + long usec = 0; qDebug("In %s", __FUNCTION__); - clearPacketList(); - //returnToQIdx = -1; - // First sort the streams by ordinalValue qSort(streamList_); - sec = 0; - usec = 0; + clearPacketList(); + for (int i = 0; i < streamList_.size(); i++) { -//_restart: if (streamList_[i]->isEnabled()) { long numPackets, numBursts; @@ -139,18 +135,17 @@ void AbstractPort::updatePacketList() if (len <= 0) continue; + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, sec, usec); + + appendToPacketList(sec, usec, pktBuf, len); + usec += ipg; if (usec > 1000000) { sec++; usec -= 1000000; } - - qDebug("q(%d, %d, %d) sec = %lu usec = %lu", - i, j, k, sec, usec); - - appendToPacketList(sec, usec, pktBuf, len); - } // for (numPackets) usec += ibg; @@ -179,7 +174,8 @@ void AbstractPort::updatePacketList() returnToQIdx = 0; */ - setPacketListLoopMode(true); + setPacketListLoopMode(true, streamList_[i]->sendUnit() == + OstProto::StreamControl::e_su_bursts ? ibg : ipg); goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_next: diff --git a/server/abstractport.h b/server/abstractport.h index e39d0f9..8df9edf 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -46,7 +46,7 @@ public: virtual void clearPacketList() = 0; virtual bool appendToPacketList(long sec, long usec, const uchar *packet, int length) = 0; - virtual void setPacketListLoopMode(bool loop) = 0; + virtual void setPacketListLoopMode(bool loop, long usecDelay) = 0; void updatePacketList(); virtual void startTransmit() = 0; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index ce576f2..83ffb4c 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -177,6 +177,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) "This Win32 platform does not support performance counter"); #endif returnToQIdx_ = -1; + loopDelay_ = 0; stop_ = false; stats_ = new AbstractPort::PortStats; usingInternalStats_ = true; @@ -210,6 +211,7 @@ void PcapPort::PortTransmitter::clearPacketList() pcap_send_queue *sq = sendQueueList_.takeFirst(); pcap_sendqueue_destroy(sq); } + setPacketListLoopMode(false, 0); } bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, @@ -223,24 +225,22 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, pktHdr.ts.tv_sec = sec; pktHdr.ts.tv_usec = usec; - if (sendQueueList_.size()) - sendQ = sendQueueList_.last(); - else - sendQ = pcap_sendqueue_alloc(1*1024*1024); - - // Not enough space? Alloc another one! - if ((sendQ->len + length + sizeof(pcap_pkthdr)) > sendQ->maxlen) + sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); + + if ((sendQ == NULL) || + (sendQ->len + sizeof(pcap_pkthdr) + length) > sendQ->maxlen) { - sendQueueList_.append(sendQ); - //! \todo (LOW): calculate sendqueue size sendQ = pcap_sendqueue_alloc(1*1024*1024); + sendQueueList_.append(sendQ); + + // Validate that the pkt will fit inside the new sendQ + Q_ASSERT((length + sizeof(pcap_pkthdr)) < sendQ->maxlen); } if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) op = false; - sendQueueList_.append(sendQ); return op; } @@ -279,6 +279,8 @@ void PcapPort::PortTransmitter::run() const int kSyncTransmit = 1; int i; + qDebug("sendQueueList_.size = %d", sendQueueList_.size()); + for(i = 0; i < sendQueueList_.size(); i++) { int ret; @@ -286,17 +288,17 @@ _restart: ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); if (ret < 0) + { + qDebug("error in sendQueueTransmit()"); return; - - //! \todo (HIGH): Timing between subsequent sendQueues + } } if (returnToQIdx_ >= 0) { i = returnToQIdx_; - //! \todo (HIGH) 1s fixed; Change this to ipg of last stream - QThread::usleep(1000000); + udelay(loopDelay_); goto _restart; } } @@ -327,11 +329,14 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, return -2; } + // A pktLen of size 0 is used at the end of a sendQueue and before + // the next sendQueue - i.e. for inter sendQueue timing if(pktLen > 0) + { pcap_sendpacket(p, pkt, pktLen); - - stats_->txPkts++; - stats_->txBytes += pktLen; + stats_->txPkts++; + stats_->txBytes += pktLen; + } // Step to the next packet in the buffer hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); diff --git a/server/pcapport.h b/server/pcapport.h index 423d15f..bcef3cc 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -18,14 +18,14 @@ public: virtual void clearPacketList() { transmitter_->clearPacketList(); - setPacketListLoopMode(false); + setPacketListLoopMode(false, 0); } virtual bool appendToPacketList(long sec, long usec, const uchar *packet, int length) { return transmitter_->appendToPacketList(sec, usec, packet, length); } - virtual void setPacketListLoopMode(bool loop) { - transmitter_->setPacketListLoopMode(loop); + virtual void setPacketListLoopMode(bool loop, long usecDelay) { + transmitter_->setPacketListLoopMode(loop, usecDelay); } virtual void startTransmit() { @@ -73,8 +73,9 @@ protected: void clearPacketList(); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); - void setPacketListLoopMode(bool loop) { + void setPacketListLoopMode(bool loop, long usecDelay) { returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = usecDelay; } void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); @@ -87,6 +88,7 @@ protected: quint64 ticksFreq_; QList sendQueueList_; int returnToQIdx_; + long loopDelay_; bool usingInternalStats_; AbstractPort::PortStats *stats_; bool usingInternalHandle_; From 70ca42fbb3fdea5506b16974343999fb8eff37ea Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 10 Jan 2010 14:40:33 +0000 Subject: [PATCH 045/294] Enhancements - Server now updates the "packet list" after every "apply" rather than before a "start transmit" (if dirty) - this better reflects user's expectation from these operations - Client disables the entire application and changes to a "Busy" cursor when "applying" stream configuration Fixes - UDP checksum no longer is zero but a valid value - Order of streams no longer gets messed up across a "apply" --- client/main.cpp | 13 +++++++++---- client/port.cpp | 4 ++-- client/portgroup.cpp | 21 ++++++++++++++++----- common/streambase.cpp | 9 ++++----- common/streambase.h | 4 ++-- common/udp.cpp | 1 + server/abstractport.cpp | 8 +++++++- server/abstractport.h | 1 + server/myservice.cpp | 5 ++++- server/pcapport.h | 3 +-- 10 files changed, 47 insertions(+), 22 deletions(-) diff --git a/client/main.cpp b/client/main.cpp index 994952a..53b43a6 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -2,11 +2,16 @@ #include +QMainWindow *mainWindow; + int main(int argc, char* argv[]) { - QApplication app(argc, argv); - MainWindow mainWin; + QApplication app(argc, argv); + int exitCode; - mainWin.show(); - return app.exec(); + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + delete mainWindow; + return exitCode; } diff --git a/client/port.cpp b/client/port.cpp index fd091f4..1606484 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -39,7 +39,7 @@ void Port::updateStreamOrdinalsFromIndex() void Port::reorderStreamsByOrdinals() { - qSort(mStreams); + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); } bool Port::newStreamAt(int index) @@ -176,7 +176,7 @@ void Port::getModifiedStreamsSinceLastSync( void Port::when_syncComplete() { - qSort(mStreams); + //reorderStreamsByOrdinals(); mLastSyncStreamList.clear(); for (int i=0; i -#include -#include - #include "portgroup.h" +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; quint32 PortGroup::mPortGroupAllocId = 0; @@ -129,6 +133,9 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) { OstProto::StreamIdList streamIdList; + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + qDebug("applying 'deleted streams' ..."); streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); @@ -177,6 +184,10 @@ void PortGroup::when_configApply(int portIndex, uint *cookie) qDebug("apply completed"); mPorts[portIndex]->when_syncComplete(); delete cookie; + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + break; default: @@ -431,7 +442,7 @@ _exit: return; } -void PortGroup::processModifyStreamAck(OstProto::Ack */*ack*/) +void PortGroup::processModifyStreamAck(OstProto::Ack * /*ack*/) { qDebug("In %s", __FUNCTION__); diff --git a/common/streambase.cpp b/common/streambase.cpp index a7320ee..a09093b 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -113,11 +113,6 @@ ProtocolListIterator* StreamBase::createProtocolListIterator() const return new ProtocolListIterator(*currentFrameProtocols); } -bool StreamBase::operator < (const StreamBase &s) const -{ - return(mCore->ordinal() < s.mCore->ordinal()); -} - quint32 StreamBase::id() { return mStreamId->id(); @@ -380,3 +375,7 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const return pktLen; } +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h index dd30232..5b40b1d 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -54,8 +54,6 @@ public: e_nw_goto_id }; - bool operator < (const StreamBase &s) const; - quint32 id(); bool setId(quint32 id); @@ -114,6 +112,8 @@ public: bool isFrameVariable() const; int frameValue(uchar *buf, int bufMaxSize, int n) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); }; #endif diff --git a/common/udp.cpp b/common/udp.cpp index b83a07a..b755310 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -203,6 +203,7 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, else cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); qDebug("UDP cksum = %hu", cksum); + break; } default: cksum = 0; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index de2cecc..e16b034 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -31,6 +31,12 @@ AbstractPort::~AbstractPort() { } +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + StreamBase* AbstractPort::stream(int streamId) { for (int i = 0; i < streamList_.size(); i++) @@ -79,7 +85,7 @@ void AbstractPort::updatePacketList() qDebug("In %s", __FUNCTION__); // First sort the streams by ordinalValue - qSort(streamList_); + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); clearPacketList(); diff --git a/server/abstractport.h b/server/abstractport.h index 8df9edf..87a8d25 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -34,6 +34,7 @@ public: void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); StreamBase* stream(int streamId); bool addStream(StreamBase *stream); bool deleteStream(int streamId); diff --git a/server/myservice.cpp b/server/myservice.cpp index 123e9ce..a91be8f 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -88,7 +88,7 @@ void MyService::getStreamIdList(::google::protobuf::RpcController* controller, OstProto::StreamId *s; s = response->add_stream_id(); - s->set_id(portInfo[portId]->stream(i)->id()); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); } done->Run(); return; @@ -224,6 +224,9 @@ void MyService::modifyStream(::google::protobuf::RpcController* controller, } } + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + //! \todo(LOW): fill-in response "Ack"???? done->Run(); diff --git a/server/pcapport.h b/server/pcapport.h index bcef3cc..ed2ed70 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -29,8 +29,7 @@ public: } virtual void startTransmit() { - if (isDirty()) - updatePacketList(); + Q_ASSERT(!isDirty()); transmitter_->start(); } virtual void stopTransmit() { transmitter_->stop(); } From b28bcd3055db938df64fa94f86e698a0e02f3af5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 9 Feb 2010 15:21:52 +0000 Subject: [PATCH 046/294] Implemented "exclusive control" for a port using bindconfig (Win32 only). Following changes done for the same - - OstProto service has a new method "modifyPort()" - At port init port.isExclusive is now set using a bindconfig query (Win32 only) - AbstractPort interface has 2 new pure virtual methods - hasExclusiveControl() and setExclusiveControl() - PcapPort does not support this functionality (yet) so these methods return false - WinPcapPort suppots this new functionality using bindconfig - Port's notes (specifying Rx/Tx limitations) are now set and updated based on hasExclusiveControl() - Presence of 'notes' on a port is indicated using a '*' after the port name in the port stats window - The tabwidget has been removed from Port Window | Stream View Pane - Ostinato Client has a new action in the port window's context menu for the same - Port Icon in the Port Window is decorated based on exclusive control --- client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/ostinato.qrc | 1 + client/port.cpp | 1 + client/port.h | 5 +- client/portgroup.cpp | 80 +++++++++++++++++++++++++++-- client/portgroup.h | 32 ++++++------ client/portmodel.cpp | 64 +++++++++++++----------- client/portmodel.h | 41 ++++++++------- client/portstatsmodel.cpp | 11 +++- client/portswindow.cpp | 30 +++++++++-- client/portswindow.h | 2 + client/portswindow.ui | 86 ++++++++++++-------------------- common/protocol.proto | 7 ++- rpc/pbrpcchannel.cpp | 8 +-- rpc/rpcserver.cpp | 2 +- server/abstractport.cpp | 20 +++++++- server/abstractport.h | 8 ++- server/myservice.cpp | 24 +++++++++ server/myservice.h | 4 ++ server/pcapport.cpp | 33 +++++++----- server/pcapport.h | 8 ++- server/winpcapport.cpp | 39 +++++++++++++++ server/winpcapport.h | 2 + 23 files changed, 356 insertions(+), 152 deletions(-) create mode 100644 client/icons/deco_exclusive.png diff --git a/client/icons/deco_exclusive.png b/client/icons/deco_exclusive.png new file mode 100644 index 0000000000000000000000000000000000000000..911da3f1d31fca4494a4beb22014e5c5c724c236 GIT binary patch literal 793 zcmV+!1LpjRP)`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/ostinato.qrc b/client/ostinato.qrc index ed5d604..5f4df48 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -12,6 +12,7 @@ icons/bullet_yellow.png icons/control_play.png icons/control_stop.png + icons/deco_exclusive.png icons/delete.png icons/magnifier.png icons/portgroup_add.png diff --git a/client/port.cpp b/client/port.cpp index 1606484..99e3112 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -22,6 +22,7 @@ Port::Port(quint32 id, quint32 portGroupId) Port::~Port() { + qDebug("%s", __FUNCTION__); while (!mStreams.isEmpty()) delete mStreams.takeFirst(); } diff --git a/client/port.h b/client/port.h index 2c36660..6c09e16 100644 --- a/client/port.h +++ b/client/port.h @@ -31,7 +31,6 @@ class Port : public QObject { public: enum AdminStatus { AdminDisable, AdminEnable }; - enum ControlMode { ControlShared, ControlExclusive }; // FIXME(HIGH): default args is a hack for QList operations on Port Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); @@ -50,8 +49,8 @@ public: { return QString().fromStdString(d.notes()); } AdminStatus adminStatus() { return (d.is_enabled()?AdminEnable:AdminDisable); } - ControlMode controlMode() - { return (d.is_exclusive_control()?ControlExclusive:ControlShared); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString &alias) { mUserAlias = alias; } diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 769b9bb..f55470b 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -32,7 +32,7 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) // FIXME(LOW):Can't for my life figure out why this ain't working! //QMetaObject::connectSlotsByName(this); connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(on_rpcChannel_stateChanged())); + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); connect(rpcChannel, SIGNAL(connected()), this, SLOT(on_rpcChannel_connected())); connect(rpcChannel, SIGNAL(disconnected()), @@ -56,9 +56,9 @@ PortGroup::~PortGroup() // ------------------------------------------------ // Slots // ------------------------------------------------ -void PortGroup::on_rpcChannel_stateChanged() +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) { - qDebug("state changed"); + qDebug("state changed %d", state); emit portGroupDataChanged(mPortGroupId); } @@ -275,6 +275,80 @@ _error_exit: delete portConfigList; } +void PortGroup::modifyPort(int portIndex, bool isExclusive) +{ + OstProto::PortConfigList portConfigList; + OstProto::Port *port; + OstProto::Ack *ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + port = portConfigList.add_port(); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + port->set_is_exclusive_control(isExclusive); + + ack = new OstProto::Ack; + rpcController->Reset(); + serviceStub->modifyPort(rpcController, &portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, portIndex, ack)); +} + +void PortGroup::processModifyPortAck(int portIndex, OstProto::Ack *ack) +{ + OstProto::PortIdList portIdList; + OstProto::PortId *portId; + OstProto::PortConfigList *portConfigList; + + qDebug("In %s", __FUNCTION__); + + portId = portIdList.add_port_id(); + portId->set_id(mPorts[portIndex]->id()); + + portConfigList = new OstProto::PortConfigList; + + rpcController->Reset(); + serviceStub->getPortConfig(rpcController, &portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + portConfigList)); + + delete ack; +} + +void PortGroup::processUpdatedPortConfig(OstProto::PortConfigList *portConfigList) +{ + qDebug("In %s", __FUNCTION__); + + if (rpcController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portGroupDataChanged(mPortGroupId); + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete portConfigList; +} + void PortGroup::getStreamIdList(int portIndex, OstProto::StreamIdList *streamIdList) { diff --git a/client/portgroup.h b/client/portgroup.h index 4566b46..f4bc502 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -23,21 +23,19 @@ class PortGroup : public QObject { Q_OBJECT private: - quint32 mPortGroupId; - static quint32 mPortGroupAllocId; - QString mUserAlias; // user defined -#if 0 // PB - QTcpSocket *mpSocket; - QHostAddress mServerAddress; - quint16 mServerPort; -#endif - PbRpcChannel *rpcChannel; - PbRpcController *rpcController; - PbRpcController *rpcControllerStats; - bool isGetStatsPending_; - ::OstProto::OstService::Stub *serviceStub; + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + PbRpcChannel *rpcChannel; + PbRpcController *rpcController; + PbRpcController *rpcControllerStats; + bool isGetStatsPending_; + + ::OstProto::OstService::Stub *serviceStub; + + ::OstProto::PortIdList portIdList; - ::OstProto::PortIdList portIdList; public: // FIXME(HIGH): member access QList mPorts; @@ -67,6 +65,10 @@ public: void processPortIdList(OstProto::PortIdList *portIdList); void processPortConfigList(OstProto::PortConfigList *portConfigList); + void modifyPort(int portId, bool isExclusive); + void processModifyPortAck(int portIndex, OstProto::Ack *ack); + void processUpdatedPortConfig(OstProto::PortConfigList *portConfigList); + void getStreamIdList(int portIndex = 0, OstProto::StreamIdList *streamIdList = NULL); void getStreamConfigList(int portIndex = 0, @@ -98,7 +100,7 @@ signals: void statsChanged(quint32 portGroupId); private slots: - void on_rpcChannel_stateChanged(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); void on_rpcChannel_connected(); void on_rpcChannel_disconnected(); void on_rpcChannel_error(QAbstractSocket::SocketError socketError); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 241090d..cb62664 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -1,6 +1,8 @@ #include "portmodel.h" #include "portgrouplist.h" + #include +#include #if 0 #define DBG0(x) qDebug(x) @@ -14,6 +16,23 @@ PortModel::PortModel(PortGroupList *p, QObject *parent) : QAbstractItemModel(parent) { pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } } int PortModel::rowCount(const QModelIndex &parent) const @@ -123,38 +142,27 @@ QVariant PortModel::data(const QModelIndex &index, int role) const } else { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + // Non Top Level - Port if ((role == Qt::DisplayRole)) { - DBG0("Exit PortModel data 4\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); - - return QString("Port %1: %2 [%3] (%4)"). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->id()). - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->name()). - arg(QHostAddress("0.0.0.0").toString()). // FIXME(LOW) - arg(pgl->mPortGroups.at( - parent.row())->mPorts[index.row()]->description()); + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); } else if ((role == Qt::DecorationRole)) { - DBG0("Exit PortModel data 5\n"); - if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) - return QVariant(); - switch(pgl->mPortGroups.at(parent.row())->mPorts[index.row()]->linkState()) - { - case OstProto::LinkStateUnknown: - return QIcon(":/icons/bullet_white.png"); - case OstProto::LinkStateDown: - return QIcon(":/icons/bullet_red.png"); - case OstProto::LinkStateUp: - return QIcon(":/icons/bullet_green.png"); - default: - qFatal("unexpected/unimplemented port oper state"); - } + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; } else { @@ -235,7 +243,7 @@ QModelIndex PortModel::parent(const QModelIndex &index) const bool PortModel::isPortGroup(const QModelIndex& index) { - if ((index.internalId() & 0xFFFF) == 0xFFFF) + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) return true; else return false; @@ -243,7 +251,7 @@ bool PortModel::isPortGroup(const QModelIndex& index) bool PortModel::isPort(const QModelIndex& index) { - if ((index.internalId() & 0xFFFF) != 0xFFFF) + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) return true; else return false; diff --git a/client/portmodel.h b/client/portmodel.h index 7f3b821..566785c 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -2,6 +2,7 @@ #define _PORT_MODEL_H #include +#include class PortGroupList; class PortGroup; @@ -12,24 +13,29 @@ class PortModel : public QAbstractItemModel friend class PortGroupList; +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: PortGroupList *pgl; - - public: - PortModel(PortGroupList *p, QObject *parent = 0); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent(const QModelIndex &index) const; - - bool isPortGroup(const QModelIndex& index); - bool isPort(const QModelIndex& index); - quint32 portGroupId(const QModelIndex& index); - quint32 portId(const QModelIndex& index); - + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; private slots: void when_portGroupDataChanged(int portGroupId, int portId); @@ -45,7 +51,6 @@ private slots: void triggerLayoutAboutToBeChanged(); void triggerLayoutChanged(); #endif - }; #endif diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index cc76c1f..80241cf 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -193,9 +193,18 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in if (orientation == Qt::Horizontal) { uint portGroupIdx, portIdx; + QString portName; getDomainIndexes(index(0, section), portGroupIdx, portIdx); - return QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + portName = QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; } else return PortStatName.at(section); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 505891f..bc52e91 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -16,6 +16,8 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) setupUi(this); + tvPortList->header()->hide(); + tvStreamList->setItemDelegate(delegate); tvStreamList->verticalHeader()->setDefaultSectionSize( @@ -27,6 +29,8 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvPortList->addAction(actionConnect_Port_Group); tvPortList->addAction(actionDisconnect_Port_Group); + tvPortList->addAction(actionExclusive_Control); + tvStreamList->addAction(actionNew_Stream); tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDelete_Stream); @@ -210,6 +214,8 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) actionDelete_Port_Group->setDisabled(true); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); goto _EXIT; } @@ -219,6 +225,9 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) if (plm->isPortGroup(current)) { actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + switch(plm->portGroup(current).state()) { case QAbstractSocket::UnconnectedState: @@ -247,9 +256,15 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) } else if (plm->isPort(current)) { - actionDelete_Port_Group->setEnabled(false); - actionConnect_Port_Group->setEnabled(false); - actionDisconnect_Port_Group->setEnabled(false); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); } _EXIT: @@ -346,6 +361,15 @@ void PortsWindow::on_actionDisconnect_Port_Group_triggered() if (current.isValid()) plm->portGroup(current).disconnectFromHost(); } + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + plm->portGroup(current.parent()).modifyPort(current.row(), checked); +} + #if 0 void PortsWindow::on_actionNew_Stream_triggered() { diff --git a/client/portswindow.h b/client/portswindow.h index e327c49..6389887 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -51,6 +51,8 @@ private slots: void on_actionConnect_Port_Group_triggered(); void on_actionDisconnect_Port_Group_triggered(); + void on_actionExclusive_Control_triggered(bool checked); + void on_actionNew_Stream_triggered(); void on_actionEdit_Stream_triggered(); void on_actionDelete_Stream_triggered(); diff --git a/client/portswindow.ui b/client/portswindow.ui index 9e461a2..17361d4 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -6,7 +6,7 @@ 0 0 689 - 254 + 352 @@ -34,7 +34,10 @@ 0 - + + + 6 + 0 @@ -47,13 +50,7 @@ 0 - - 6 - - - 6 - - + @@ -77,7 +74,7 @@ - + QFrame::StyledPanel @@ -129,52 +126,23 @@ - - - - 0 + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows - - - Control - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::ActionsContextMenu - - - QFrame::NoFrame - - - 0 - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - - -
@@ -254,6 +222,14 @@ Edit Stream + + + true + + + Exclusive Control (EXPERIMENTAL) + + diff --git a/common/protocol.proto b/common/protocol.proto index 86ceb59..d1205f7 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -1,9 +1,7 @@ -// stream.proto - package OstProto; message StreamId { - required uint32 id = 1; + required uint32 id = 1; } message StreamCore { @@ -136,7 +134,7 @@ message PortConfigList { } message StreamConfigList { - required PortId port_id = 1; + required PortId port_id = 1; repeated Stream stream = 2; } @@ -188,6 +186,7 @@ message PortStatsList { service OstService { rpc getPortIdList(Void) returns (PortIdList); rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); rpc getStreamIdList(PortId) returns (StreamIdList); rpc getStreamConfig(StreamIdList) returns (StreamConfigList); diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index a06d14e..6268320 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -64,7 +64,7 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Closure* done) { char msg[MSGBUF_SIZE]; - int len; + int len; bool ret; if (isPending) @@ -80,6 +80,7 @@ void PbRpcChannel::CallMethod( call.done = done; pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); Q_ASSERT(pendingCallList.size() < 100); @@ -113,7 +114,7 @@ void PbRpcChannel::CallMethod( *((quint32*)(&msg[4])) = HTONL(len); // len // Avoid printing stats since it happens every couple of seconds - if (pendingMethodId != 12) + if (pendingMethodId != 13) { qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, PB_HDR_SIZE + len, req->DebugString().c_str()); @@ -230,7 +231,7 @@ void PbRpcChannel::on_mpSocket_readyRead() response->ParseFromArray((void*) msg, len); // Avoid printing stats - if (method != 12) + if (method != 13) { qDebug("client(%s): Parsed as %s", __FUNCTION__, response->DebugString().c_str()); @@ -262,6 +263,7 @@ void PbRpcChannel::on_mpSocket_readyRead() if (pendingCallList.size()) { RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d", call.method->index()); CallMethod(call.method, call.controller, call.request, call.response, call.done); } diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index c989946..bb212ad 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -104,7 +104,7 @@ void RpcServer::done(::google::protobuf::Message *request, *((quint32*)(&msg[4])) = HTONL(len); // len // Avoid printing stats since it happens once every couple of seconds - if (pendingMethodId != 12) + if (pendingMethodId != 13) { qDebug("Server(%s): sending %d bytes to client encoding <%s>", __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); diff --git a/server/abstractport.cpp b/server/abstractport.cpp index e16b034..7d2c0cf 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -13,7 +13,6 @@ AbstractPort::AbstractPort(int id, const char *device) //! \todo (LOW) admin enable/disable of port data_.set_is_enabled(true); - //! \todo (HIGH) port exclusive control data_.set_is_exclusive_control(false); isSendQueueDirty_ = true; @@ -23,12 +22,29 @@ AbstractPort::AbstractPort(int id, const char *device) resetStats(); } +AbstractPort::~AbstractPort() +{ +} + void AbstractPort::init() { } -AbstractPort::~AbstractPort() +bool AbstractPort::modify(const OstProto::Port &port) { + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + return ret; } StreamBase* AbstractPort::streamAtIndex(int index) diff --git a/server/abstractport.h b/server/abstractport.h index 87a8d25..ba13d25 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -33,6 +33,12 @@ public: int id() { return data_.port_id().id(); } void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + int streamCount() { return streamList_.size(); } StreamBase* streamAtIndex(int index); StreamBase* stream(int streamId); @@ -42,8 +48,6 @@ public: bool isDirty() { return isSendQueueDirty_; } void setDirty() { isSendQueueDirty_ = true; } - virtual OstProto::LinkState linkState() { return linkState_; } - virtual void clearPacketList() = 0; virtual bool appendToPacketList(long sec, long usec, const uchar *packet, int length) = 0; diff --git a/server/myservice.cpp b/server/myservice.cpp index a91be8f..6a250a0 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -69,6 +69,30 @@ void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, done->Run(); } +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + void MyService::getStreamIdList(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::StreamIdList* response, diff --git a/server/myservice.h b/server/myservice.h index a813367..4122c5a 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -25,6 +25,10 @@ public: const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done); + void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); virtual void getStreamIdList(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::StreamIdList* response, diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 83ffb4c..8315fa8 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -44,24 +44,12 @@ PcapPort::PcapPort(int id, const char *device) void PcapPort::init() { - QString notes; - - if (!monitorRx_->isDirectional() && !data_.is_exclusive_control()) - notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
"); - if (!monitorTx_->isDirectional()) - { transmitter_->useExternalStats(&stats_); - notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
"); - } transmitter_->setHandle(monitorRx_->handle()); - if (!notes.isEmpty()) - data_.set_notes(QString("Limitation(s)" - "

%1
" - "Rx/Tx Rates are also subject to above limitation(s)

"). - arg(notes).toStdString()); + updateNotes(); monitorRx_->start(); monitorTx_->start(); @@ -75,6 +63,25 @@ PcapPort::~PcapPort() delete monitorRx_; } +void PcapPort::updateNotes() +{ + QString notes; + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
"); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
"); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "

%1
" + "Rx/Tx Rates are also subject to above limitation(s)

"). + arg(notes).toStdString()); +} + PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) { diff --git a/server/pcapport.h b/server/pcapport.h index ed2ed70..cd91889 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -16,6 +16,9 @@ public: void init(); + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool exclusive) { return false; } + virtual void clearPacketList() { transmitter_->clearPacketList(); setPacketListLoopMode(false, 0); @@ -114,11 +117,14 @@ protected: PortMonitor *monitorRx_; PortMonitor *monitorTx_; + + void updateNotes(); + private: PortTransmitter *transmitter_; PortCapturer *capturer_; - static pcap_if_t *deviceList_; + static pcap_if_t *deviceList_; }; #endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 8afefba..4598489 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -1,5 +1,7 @@ #include "winpcapport.h" +#include + #ifdef Q_OS_WIN32 const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; @@ -20,6 +22,8 @@ WinPcapPort::WinPcapPort(int id, const char *device) sizeof(uint)); if (!linkStateOid_) qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); } WinPcapPort::~WinPcapPort() @@ -53,6 +57,41 @@ OstProto::LinkState WinPcapPort::linkState() return linkState_; } +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + exitCode = QProcess::execute("bindconfig.exe", + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + status = exclusive ? "disable" : "enable"; + + QProcess::execute("bindconfig.exe", + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) : PcapPort::PortMonitor(device, direction, stats) diff --git a/server/winpcapport.h b/server/winpcapport.h index 80a83c9..dc0c84d 100644 --- a/server/winpcapport.h +++ b/server/winpcapport.h @@ -16,6 +16,8 @@ public: ~WinPcapPort(); virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); protected: class PortMonitor: public PcapPort::PortMonitor From 2581562ec506ed472fdb712328b52cf1ebd3dd1c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 17 Feb 2010 15:26:42 +0000 Subject: [PATCH 047/294] Fixes - Queued RPC calls would cause crashes due to invalid pointers to request/response and/or controller; this has been fixed - PbRpcController now takes ownership of request and response messages and will delete them when it itself is being deleted - This design mandates that request and response messages for each RPC call have to be allocated on the heap. - The convention for the Closure 'done' call now is to allocate and pass a pointer to the controller object to it which will delete it after use; this requires that controller itself be also allocated on the heap (NOTE: this is just a convention - not mandatory) - All existing RPC calls (in portgroup.cpp) have been changed to follow the above convention - Reordering of queued RPC calls has been fixed - PortManager is now destroyed at exit; because of this fix the per port temporary capture files are auto-removed at exit - WinPcapPort destructor no longer deletes the monitor threads because the parent class PcapPort already does it - Capture does not automatically (and incorrectly) stop after one packet if started immediately after a View Capture operation - User is prompted to stop transmit on a port first if he tries to apply configuration changes on a port in 'transmit' state --- client/port.cpp | 1 + client/port.h | 12 +- client/portgroup.cpp | 748 ++++++++++++++++++--------------------- client/portgroup.h | 52 ++- client/portgrouplist.cpp | 7 +- client/portswindow.cpp | 8 + rpc/pbrpcchannel.cpp | 12 +- rpc/pbrpccontroller.h | 29 +- rpc/rpcserver.cpp | 19 +- rpc/rpcserver.h | 4 +- server/myservice.cpp | 3 + server/pcapport.cpp | 13 +- server/pcapport.h | 4 +- server/winpcapport.cpp | 2 - 14 files changed, 441 insertions(+), 473 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 99e3112..565af93 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -18,6 +18,7 @@ Port::Port(quint32 id, quint32 portGroupId) d.mutable_port_id()->set_id(id); stats.mutable_port_id()->set_id(id); mPortGroupId = portGroupId; + capFile_ = NULL; } Port::~Port() diff --git a/client/port.h b/client/port.h index 6c09e16..6e2e9d3 100644 --- a/client/port.h +++ b/client/port.h @@ -1,9 +1,10 @@ #ifndef _PORT_H #define _PORT_H -#include -#include #include +#include +#include + #include "stream.h" //class StreamModel; @@ -16,6 +17,7 @@ class Port : public QObject { OstProto::Port d; OstProto::PortStats stats; + QTemporaryFile *capFile_; // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' quint32 mPortId; @@ -66,6 +68,12 @@ public: { return stats.state().link_state(); } OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal void updatePortConfig(OstProto::Port *port); diff --git a/client/portgroup.cpp b/client/portgroup.cpp index f55470b..7531ef6 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -7,26 +7,24 @@ #include #include +using ::google::protobuf::NewCallback; + extern QMainWindow *mainWindow; -quint32 PortGroup::mPortGroupAllocId = 0; +quint32 PortGroup::mPortGroupAllocId = 0; PortGroup::PortGroup(QHostAddress ip, quint16 port) { // Allocate an id for self mPortGroupId = PortGroup::mPortGroupAllocId++; - rpcChannel = new PbRpcChannel(ip, port); + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; - /*! - \todo (HIGH) RPC Controller should be allocated and deleted for each RPC invocation - as implemented currently, if a RPC is invoked before the previous completes, - rpc controller is overwritten due to the Reset() call - maybe we need to pass the - pointer to the controller to the callback function also? - */ - rpcController = new PbRpcController; - rpcControllerStats = new PbRpcController; + statsController = new PbRpcController(portIdList_, portStatsList_); isGetStatsPending_ = false; + + rpcChannel = new PbRpcChannel(ip, port); serviceStub = new OstProto::OstService::Stub(rpcChannel); // FIXME(LOW):Can't for my life figure out why this ain't working! @@ -47,9 +45,8 @@ PortGroup::~PortGroup() // Disconnect and free rpc channel etc. PortGroup::disconnectFromHost(); delete serviceStub; - delete rpcControllerStats; - delete rpcController; delete rpcChannel; + delete statsController; } @@ -64,17 +61,17 @@ void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) void PortGroup::on_rpcChannel_connected() { - OstProto::Void void_; - OstProto::PortIdList *portIdList; + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; qDebug("connected\n"); emit portGroupDataChanged(mPortGroupId); qDebug("requesting portlist ..."); - portIdList = new OstProto::PortIdList(); - rpcController->Reset(); - serviceStub->getPortIdList(rpcController, &void_, portIdList, - NewCallback(this, &PortGroup::processPortIdList, portIdList)); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); } void PortGroup::on_rpcChannel_disconnected() @@ -95,112 +92,16 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) emit portGroupDataChanged(mPortGroupId); } -void PortGroup::when_configApply(int portIndex, uint *cookie) +void PortGroup::processPortIdList(PbRpcController *controller) { - uint *op; - OstProto::Ack *ack; + OstProto::PortIdList *portIdList + = static_cast(controller->response()); - Q_ASSERT(portIndex < mPorts.size()); + Q_ASSERT(portIdList != NULL); - if (state() != QAbstractSocket::ConnectedState) - { - if (cookie != NULL) - delete cookie; - return; - } - - if (cookie == NULL) - { - // cookie[0]: op [0 - delete, 1 - add, 2 - modify, 3 - Done!] - // cookie[1]: *ack - cookie = new uint[2]; - ack = new OstProto::Ack; - - cookie[0] = (uint) 0; - cookie[1] = (uint) ack; - } - else - { - ack = (OstProto::Ack*) cookie[1]; - } - - Q_ASSERT(cookie != NULL); - op = &cookie[0]; - - switch (*op) - { - case 0: - { - OstProto::StreamIdList streamIdList; - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - mainWindow->setDisabled(true); - - qDebug("applying 'deleted streams' ..."); - - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getDeletedStreamsSinceLastSync(streamIdList); - - (*op)++; - rpcController->Reset(); - serviceStub->deleteStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 1: - { - OstProto::StreamIdList streamIdList; - - qDebug("applying 'new streams' ..."); - - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getNewStreamsSinceLastSync(streamIdList); - - (*op)++; - rpcController->Reset(); - serviceStub->addStream(rpcController, &streamIdList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 2: - { - OstProto::StreamConfigList streamConfigList; - - qDebug("applying 'modified streams' ..."); - - streamConfigList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - mPorts[portIndex]->getModifiedStreamsSinceLastSync(streamConfigList); - - (*op)++; - rpcController->Reset(); - serviceStub->modifyStream(rpcController, &streamConfigList, ack, - ::google::protobuf::NewCallback(this, &PortGroup::when_configApply, portIndex, cookie)); - break; - } - - case 3: - qDebug("apply completed"); - mPorts[portIndex]->when_syncComplete(); - delete cookie; - - mainWindow->setEnabled(true); - QApplication::restoreOverrideCursor(); - - break; - - default: - qDebug("%s: Unknown Op!!!", __FUNCTION__); - break; - } -} - -void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) -{ qDebug("got a portlist ..."); - if (rpcController->Failed()) + if (controller->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); goto _error_exit; @@ -221,32 +122,37 @@ void PortGroup::processPortIdList(OstProto::PortIdList *portIdList) emit portListChanged(mPortGroupId); - this->portIdList.CopyFrom(*portIdList); + portIdList_->CopyFrom(*portIdList); // Request PortConfigList { - OstProto::PortConfigList *portConfigList; - qDebug("requesting port config list ..."); - portConfigList = new OstProto::PortConfigList(); - rpcController->Reset(); - serviceStub->getPortConfig(rpcController, - portIdList, portConfigList, NewCallback(this, - &PortGroup::processPortConfigList, portConfigList)); - } + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); - goto _exit; + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } _error_exit: _exit: - delete portIdList; + delete controller; } -void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) +void PortGroup::processPortConfigList(PbRpcController *controller) { + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + qDebug("In %s", __FUNCTION__); - if (rpcController->Failed()) + if (controller->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); goto _error_exit; @@ -272,14 +178,89 @@ void PortGroup::processPortConfigList(OstProto::PortConfigList *portConfigList) getStreamIdList(); _error_exit: - delete portConfigList; + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; } void PortGroup::modifyPort(int portIndex, bool isExclusive) { - OstProto::PortConfigList portConfigList; - OstProto::Port *port; - OstProto::Ack *ack; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); @@ -288,42 +269,51 @@ void PortGroup::modifyPort(int portIndex, bool isExclusive) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mainWindow->setDisabled(true); - port = portConfigList.add_port(); + OstProto::Port *port = portConfigList->add_port(); port->mutable_port_id()->set_id(mPorts[portIndex]->id()); port->set_is_exclusive_control(isExclusive); - ack = new OstProto::Ack; - rpcController->Reset(); - serviceStub->modifyPort(rpcController, &portConfigList, ack, - NewCallback(this, &PortGroup::processModifyPortAck, portIndex, ack)); + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); } -void PortGroup::processModifyPortAck(int portIndex, OstProto::Ack *ack) -{ - OstProto::PortIdList portIdList; - OstProto::PortId *portId; - OstProto::PortConfigList *portConfigList; - - qDebug("In %s", __FUNCTION__); - - portId = portIdList.add_port_id(); - portId->set_id(mPorts[portIndex]->id()); - - portConfigList = new OstProto::PortConfigList; - - rpcController->Reset(); - serviceStub->getPortConfig(rpcController, &portIdList, portConfigList, - NewCallback(this, &PortGroup::processUpdatedPortConfig, - portConfigList)); - - delete ack; -} - -void PortGroup::processUpdatedPortConfig(OstProto::PortConfigList *portConfigList) +void PortGroup::processModifyPortAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); - if (rpcController->Failed()) + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); goto _exit; @@ -346,47 +336,47 @@ void PortGroup::processUpdatedPortConfig(OstProto::PortConfigList *portConfigLis _exit: mainWindow->setEnabled(true); QApplication::restoreOverrideCursor(); - delete portConfigList; + delete controller; } -void PortGroup::getStreamIdList(int portIndex, - OstProto::StreamIdList *streamIdList) +void PortGroup::getStreamIdList() { - ::OstProto::PortId portId; - - qDebug("In %s", __FUNCTION__); - - if (streamIdList == NULL) + for (int portIndex = 0; portIndex < numPorts(); portIndex++) { - // First invocation (uses default params) - - // request StreamIdList for first port + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); - streamIdList = new ::OstProto::StreamIdList(); + portId->set_id(mPorts[portIndex]->id()); - goto _request; + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); } +} - qDebug("got a streamIdlist ..."); +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); - if (rpcController->Failed()) + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; // FIXME(MED): Partial RPC + goto _exit; } Q_ASSERT(portIndex < numPorts()); if (streamIdList->port_id().id() != mPorts[portIndex]->id()) { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamIdList->port_id().id(), mPorts[portIndex]->id(), - portIndex); - goto _next_port; // FIXME(MED): Partial RPC + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; } - // FIXME(MED): need to mPorts.clear()??? for(int i = 0; i < streamIdList->stream_id_size(); i++) { uint streamId; @@ -395,82 +385,69 @@ void PortGroup::getStreamIdList(int portIndex, mPorts[portIndex]->insertStream(streamId); } -_next_port: - // FIXME(HI): ideally we shd use signals/slots but this means - // we will have to use Port* instead of Port with QList<> - - // need to find a way for this mPorts[portIndex]->when_syncComplete(); - portIndex++; - if (portIndex >= numPorts()) + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) { - // We're done for all ports !!! - // FIXME(HI): some way to reset streammodel - - delete streamIdList; - - if (numPorts() > 0) - getStreamConfigList(); - - goto _exit; + getStreamConfigList(); } -_request: - portId.set_id(mPorts[portIndex]->id()); - streamIdList->Clear(); - - rpcController->Reset(); - serviceStub->getStreamIdList(rpcController, &portId, streamIdList, - NewCallback(this, &PortGroup::getStreamIdList, - portIndex, streamIdList)); - - goto _exit; - - - _exit: - return; + delete controller; } -void PortGroup::getStreamConfigList(int portIndex, - OstProto::StreamConfigList *streamConfigList) +void PortGroup::getStreamConfigList() { - OstProto::StreamIdList streamIdList; + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); qDebug("In %s", __PRETTY_FUNCTION__); - if (streamConfigList == NULL) - { - // First invocation using default params - // - request for first port + Q_ASSERT(portIndex < numPorts()); - Q_ASSERT(portIndex == 0); - Q_ASSERT(numPorts() > 0); - - streamConfigList = new OstProto::StreamConfigList; - - goto _request; - } - - qDebug("got a streamconfiglist"); - - if (rpcController->Failed()) + if (controller->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); - goto _next_port; + goto _exit; } Q_ASSERT(portIndex < numPorts()); if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) { - qDebug("%s: Invalid portId %d (expected %d) received for portIndex %d", - __FUNCTION__, streamConfigList->port_id().id(), - mPorts[portIndex]->id(), portIndex); - goto _next_port; // FIXME(MED): Partial RPC + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; } - // FIXME(MED): need to mStreams.clear()??? for(int i = 0; i < streamConfigList->stream_size(); i++) { uint streamId; @@ -480,84 +457,53 @@ void PortGroup::getStreamConfigList(int portIndex, streamConfigList->mutable_stream(i)); } -_next_port: - portIndex++; - + // Are we done for all ports? if (portIndex >= numPorts()) { - // We're done for all ports !!! - // FIXME(HI): some way to reset streammodel - - delete streamConfigList; - goto _exit; } -_request: - qDebug("requesting stream config list ..."); - - streamIdList.Clear(); - streamIdList.mutable_port_id()->set_id(mPorts[portIndex]->id()); - for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) - { - OstProto::StreamId *s; - - s = streamIdList.add_stream_id(); - s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); - } - streamConfigList->Clear(); - - rpcController->Reset(); - serviceStub->getStreamConfig(rpcController, - &streamIdList, streamConfigList, NewCallback(this, - &PortGroup::getStreamConfigList, portIndex, streamConfigList)); - _exit: - return; -} - -void PortGroup::processModifyStreamAck(OstProto::Ack * /*ack*/) -{ - qDebug("In %s", __FUNCTION__); - - qDebug("Modify Successful!!"); - - // TODO(HI): Apply Button should now be disabled???!!!!??? + delete controller; } void PortGroup::startTx(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) - return; + goto _exit; - ack = new OstProto::Ack; if (portList == NULL) goto _exit; - else + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + for (int i = 0; i < portList->size(); i++) { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); + OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } - } - serviceStub->startTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartTxAck, ack)); + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } _exit: return; } +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + void PortGroup::stopTx(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; qDebug("In %s", __FUNCTION__); @@ -566,28 +512,33 @@ void PortGroup::stopTx(QList *portList) if ((portList == NULL) || (portList->size() == 0)) goto _exit; - - ack = new OstProto::Ack; - - for (int i = 0; i < portList->size(); i++) { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); - rpcController->Reset(); - serviceStub->stopTx(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopTxAck, ack)); + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } _exit: return; } +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + void PortGroup::startCapture(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) @@ -596,27 +547,33 @@ void PortGroup::startCapture(QList *portList) if ((portList == NULL) || (portList->size() == 0)) goto _exit; - ack = new OstProto::Ack; - - for (int i = 0; i < portList->size(); i++) { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); - rpcController->Reset(); - serviceStub->startCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStartCaptureAck, ack)); + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } _exit: return; } +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + void PortGroup::stopCapture(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) @@ -625,25 +582,33 @@ void PortGroup::stopCapture(QList *portList) if ((portList == NULL) || (portList->size() == 0)) goto _exit; - ack = new OstProto::Ack; - for (int i = 0; i < portList->size(); i++) { - OstProto::PortId *portId; - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); - } + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); - rpcController->Reset(); - serviceStub->stopCapture(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processStopCaptureAck, ack)); + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } _exit: return; } +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + void PortGroup::viewCapture(QList *portList) { - static QTemporaryFile *capFile = NULL; - qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) @@ -652,62 +617,31 @@ void PortGroup::viewCapture(QList *portList) if ((portList == NULL) || (portList->size() != 1)) goto _exit; - if (capFile) - delete capFile; - - /*! \todo (MED) unable to reuse the same file 'coz capFile->resize(0) is - not working - it fails everytime */ - capFile = new QTemporaryFile(); - capFile->open(); - qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); for (int i = 0; i < portList->size(); i++) { - OstProto::PortId portId; - OstProto::CaptureBuffer *buf; + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); - portId.set_id(portList->at(i)); + portId->set_id(portList->at(i)); - buf = new OstProto::CaptureBuffer; - rpcController->Reset(); - rpcController->setBinaryBlob(capFile); - serviceStub->getCaptureBuffer(rpcController, &portId, buf, - NewCallback(this, &PortGroup::processViewCaptureAck, buf, (QFile*) capFile)); + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); } _exit: return; } -void PortGroup::processStartTxAck(OstProto::Ack *ack) +void PortGroup::processViewCaptureAck(PbRpcController *controller) { - qDebug("In %s", __FUNCTION__); + QFile *capFile = static_cast(controller->binaryBlob()); - delete ack; -} - -void PortGroup::processStopTxAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processStartCaptureAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processStopCaptureAck(OstProto::Ack *ack) -{ - qDebug("In %s", __FUNCTION__); - - delete ack; -} - -void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile) -{ #ifdef Q_OS_WIN32 QString viewer("C:/Program Files/Wireshark/wireshark.exe"); #else @@ -722,90 +656,90 @@ void PortGroup::processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFi if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) qDebug("Failed starting Wireshark"); - delete buf; + delete controller; } void PortGroup::getPortStats() { - OstProto::PortStatsList *portStatsList; - //qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) - return; + goto _exit; if (isGetStatsPending_) - return; + goto _exit; - portStatsList = new OstProto::PortStatsList; - rpcControllerStats->Reset(); + statsController->Reset(); isGetStatsPending_ = true; - serviceStub->getStats(rpcControllerStats, &portIdList, portStatsList, - NewCallback(this, &PortGroup::processPortStatsList, portStatsList)); + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; } -void PortGroup::processPortStatsList(OstProto::PortStatsList *portStatsList) +void PortGroup::processPortStatsList() { //qDebug("In %s", __FUNCTION__); - if (rpcControllerStats->Failed()) + if (statsController->Failed()) { qDebug("%s: rpc failed", __FUNCTION__); goto _error_exit; } - for(int i = 0; i < portStatsList->port_stats_size(); i++) + for(int i = 0; i < portStatsList_->port_stats_size(); i++) { - uint id; - - id = portStatsList->port_stats(i).port_id().id(); + uint id = portStatsList_->port_stats(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] - mPorts[id]->updateStats(portStatsList->mutable_port_stats(i)); + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); } emit statsChanged(mPortGroupId); _error_exit: - delete portStatsList; isGetStatsPending_ = false; } void PortGroup::clearPortStats(QList *portList) { - OstProto::PortIdList portIdList; - OstProto::Ack *ack; - qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) - return; + goto _exit; - ack = new OstProto::Ack; - if (portList == NULL) - portIdList.CopyFrom(this->portIdList); - else { - for (int i = 0; i < portList->size(); i++) + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else { - OstProto::PortId *portId; - - portId = portIdList.add_port_id(); - portId->set_id(portList->at(i)); + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } } - } - rpcController->Reset(); - serviceStub->clearStats(rpcController, &portIdList, ack, - NewCallback(this, &PortGroup::processClearStatsAck, ack)); + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; } -void PortGroup::processClearStatsAck(OstProto::Ack *ack) +void PortGroup::processClearStatsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); // Refresh stats immediately after a stats clear/reset getPortStats(); - delete ack; + delete controller; } diff --git a/client/portgroup.h b/client/portgroup.h index f4bc502..88e96f7 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -28,13 +28,13 @@ private: QString mUserAlias; // user defined PbRpcChannel *rpcChannel; - PbRpcController *rpcController; - PbRpcController *rpcControllerStats; + PbRpcController *statsController; bool isGetStatsPending_; - ::OstProto::OstService::Stub *serviceStub; + OstProto::OstService::Stub *serviceStub; - ::OstProto::PortIdList portIdList; + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; public: // FIXME(HIGH): member access QList mPorts; @@ -62,36 +62,40 @@ public: QAbstractSocket::SocketState state() const { return rpcChannel->state(); } - void processPortIdList(OstProto::PortIdList *portIdList); - void processPortConfigList(OstProto::PortConfigList *portConfigList); + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); void modifyPort(int portId, bool isExclusive); - void processModifyPortAck(int portIndex, OstProto::Ack *ack); - void processUpdatedPortConfig(OstProto::PortConfigList *portConfigList); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); - void getStreamIdList(int portIndex = 0, - OstProto::StreamIdList *streamIdList = NULL); - void getStreamConfigList(int portIndex = 0, - OstProto::StreamConfigList *streamConfigList = NULL); + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); void processModifyStreamAck(OstProto::Ack *ack); void startTx(QList *portList = NULL); - void processStartTxAck(OstProto::Ack *ack); + void processStartTxAck(PbRpcController *controller); void stopTx(QList *portList = NULL); - void processStopTxAck(OstProto::Ack *ack); + void processStopTxAck(PbRpcController *controller); void startCapture(QList *portList = NULL); - void processStartCaptureAck(OstProto::Ack *ack); + void processStartCaptureAck(PbRpcController *controller); void stopCapture(QList *portList = NULL); - void processStopCaptureAck(OstProto::Ack *ack); + void processStopCaptureAck(PbRpcController *controller); void viewCapture(QList *portList = NULL); - void processViewCaptureAck(OstProto::CaptureBuffer *buf, QFile *capFile); + void processViewCaptureAck(PbRpcController *controller); void getPortStats(); - void processPortStatsList(OstProto::PortStatsList *portStatsList); + void processPortStatsList(); void clearPortStats(QList *portList = NULL); - void processClearStatsAck(OstProto::Ack *ack); + void processClearStatsAck(PbRpcController *controller); signals: void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); @@ -106,16 +110,8 @@ private slots: void on_rpcChannel_error(QAbstractSocket::SocketError socketError); public slots: - void when_configApply(int portIndex, uint *cookie = NULL); -#if 0 // PB - void on_rpcChannel_when_dataAvail(); -#endif + void when_configApply(int portIndex); -private: -#if 0 // PB - void ProcessCapabilityInfo(const char *msg, qint32 size); - void ProcessMsg(const char *msg, quint32 size); -#endif }; #endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index b76fb15..6d8a079 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -22,12 +22,13 @@ PortGroupList::PortGroupList() PortGroupList::~PortGroupList() { - while (!mPortGroups.isEmpty()) - delete mPortGroups.takeFirst(); - delete portStatsModelTester_; delete portModelTester_; delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + } bool PortGroupList::isPortGroup(const QModelIndex& index) diff --git a/client/portswindow.cpp b/client/portswindow.cpp index bc52e91..f3b4760 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "streamconfigdialog.h" #include "streamlistdelegate.h" @@ -289,6 +290,13 @@ void PortsWindow::on_pbApply_clicked() goto _exit; } + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + curPortGroup = plm->getPortModel()->parent(curPort); if (!curPortGroup.isValid()) { diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 6268320..9b5997f 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -70,8 +70,9 @@ void PbRpcChannel::CallMethod( if (isPending) { RpcCall call; - qDebug("RpcChannel: queueing method %d since %d is pending", - method->index(), pendingMethodId); + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); call.method = method; call.controller = controller; @@ -252,18 +253,19 @@ void PbRpcChannel::on_mpSocket_readyRead() } + done->Run(); + pendingMethodId = -1; controller = NULL; response = NULL; isPending = false; parsing = false; - done->Run(); - if (pendingCallList.size()) { RpcCall call = pendingCallList.takeFirst(); - qDebug("RpcChannel: executing queued method %d", call.method->index()); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); CallMethod(call.method, call.controller, call.request, call.response, call.done); } diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 6eef8cd..50260d7 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -5,17 +5,26 @@ class QIODevice; +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ class PbRpcController : public ::google::protobuf::RpcController { - bool failed; - QIODevice *blob; - std::string errStr; - public: - PbRpcController() { Reset(); } + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } // Client Side Methods - void Reset() { failed=false; blob = NULL; } + void Reset() { failed = false; blob = NULL; } bool Failed() const { return failed; } void StartCancel() { /*! \todo (MED) */} std::string ErrorText() const { return errStr; } @@ -31,6 +40,14 @@ public: // srivatsp added QIODevice* binaryBlob() { return blob; }; void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + }; #endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index bb212ad..36d96e9 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -10,7 +10,6 @@ RpcServer::RpcServer() isPending = false; pendingMethodId = -1; // don't care as long as isPending is false - controller_.Reset(); } RpcServer::~RpcServer() @@ -47,22 +46,22 @@ QString RpcServer::errorString() return errorString_; } -void RpcServer::done(::google::protobuf::Message *request, - ::google::protobuf::Message *response) +void RpcServer::done(PbRpcController *controller) { + google::protobuf::Message *response = controller->response(); QIODevice *blob; char msg[MSGBUF_SIZE]; int len; //qDebug("In RpcServer::done"); - if (controller_.Failed()) + if (controller->Failed()) { qDebug("rpc failed"); goto _exit; } - blob = controller_.binaryBlob(); + blob = controller->binaryBlob(); if (blob) { len = blob->size(); @@ -114,8 +113,7 @@ void RpcServer::done(::google::protobuf::Message *request, clientSock->write(msg, PB_HDR_SIZE + len); _exit: - delete request; - delete response; + delete controller; isPending = false; } @@ -176,6 +174,7 @@ void RpcServer::when_dataAvail() static quint32 len; const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; + PbRpcController *controller; if (!parsing) { @@ -240,12 +239,12 @@ void RpcServer::when_dataAvail() //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, //resp->DebugString().c_str()); - controller_.Reset(); + controller = new PbRpcController(req, resp); //qDebug("before service->callmethod()"); - service->CallMethod(methodDesc, &controller_, req, resp, - NewCallback(this, &RpcServer::done, req, resp)); + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); parsing = false; diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 9b8cd23..0872373 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -23,7 +23,6 @@ class RpcServer : public QObject bool isPending; int pendingMethodId; - PbRpcController controller_; QString errorString_; public: @@ -33,8 +32,7 @@ public: bool registerService(::google::protobuf::Service *service, quint16 tcpPortNum); QString errorString(); - void done(::google::protobuf::Message *req, - ::google::protobuf::Message *resp); + void done(PbRpcController *controller); private slots: void when_newConnection(); diff --git a/server/myservice.cpp b/server/myservice.cpp index 6a250a0..3699cfd 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -25,6 +25,9 @@ MyService::MyService() MyService::~MyService() { + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); } void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 8315fa8..8486702 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -57,6 +57,7 @@ void PcapPort::init() PcapPort::~PcapPort() { + qDebug("In %s", __FUNCTION__); delete capturer_; delete transmitter_; delete monitorTx_; @@ -296,7 +297,8 @@ _restart: if (ret < 0) { - qDebug("error in sendQueueTransmit()"); + qDebug("error %d in sendQueueTransmit()", ret); + stop_ = false; return; } } @@ -312,7 +314,8 @@ _restart: void PcapPort::PortTransmitter::stop() { - stop_ = true; + if (isRunning()) + stop_ = true; } int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, @@ -332,7 +335,6 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, if (stop_) { - stop_ = false; return -2; } @@ -453,7 +455,6 @@ void PcapPort::PortCapturer::run() if (stop_) { qDebug("user requested capture stop\n"); - stop_ = false; break; } } @@ -461,11 +462,13 @@ void PcapPort::PortCapturer::run() pcap_close(handle_); dumpHandle_ = NULL; handle_ = NULL; + stop_ = false; } void PcapPort::PortCapturer::stop() { - stop_ = true; + if (isRunning()) + stop_ = true; } QFile* PcapPort::PortCapturer::captureFile() diff --git a/server/pcapport.h b/server/pcapport.h index cd91889..5a1ef3f 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -95,7 +95,7 @@ protected: AbstractPort::PortStats *stats_; bool usingInternalHandle_; pcap_t *handle_; - bool stop_; + volatile bool stop_; }; class PortCapturer: public QThread @@ -109,7 +109,7 @@ protected: private: QString device_; - bool stop_; + volatile bool stop_; QTemporaryFile capFile_; pcap_t *handle_; pcap_dumper_t *dumpHandle_; diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 4598489..b9ddfdf 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -28,8 +28,6 @@ WinPcapPort::WinPcapPort(int id, const char *device) WinPcapPort::~WinPcapPort() { - delete monitorRx_; - delete monitorTx_; } OstProto::LinkState WinPcapPort::linkState() From 3281eb8f2018943918b89e533d2b2fec2c711d79 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 6 Mar 2010 09:20:22 +0000 Subject: [PATCH 048/294] - Build files cleanup - Top level Makefile now has a 'release' target - pcap_set_direction() is no longer invoked in case of Win32 as older versions of WinPcap do not support this API - MyService::modifyPort() declaration modified to fix the error reported by newer gcc compiler --- Makefile | 13 ++++++++----- client/ostinato.pro | 27 ++++++++++++++++++++------- server/drone.pro | 31 ++++++++++++++++++++++--------- server/myservice.h | 2 +- server/pcapport.cpp | 9 ++++++++- 5 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 574c0a1..698cf1b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -all: +release: QMAKE_CONFIG=-config release + +all release: qmake $(MAKE) -C rpc $(MAKE) -C common $(MAKE) -C server @@ -17,7 +19,8 @@ distclean: $(MAKE) -C client $@ qmake: - cd rpc && qmake && cd .. - cd common && qmake && cd .. - cd server && qmake && cd .. - cd client && qmake && cd .. + cd rpc && qmake $(QMAKE_CONFIG) && cd .. + cd common && qmake $(QMAKE_CONFIG) && cd .. + cd server && qmake $(QMAKE_CONFIG) && cd .. + cd client && qmake $(QMAKE_CONFIG) && cd .. + diff --git a/client/ostinato.pro b/client/ostinato.pro index a644eb9..c24cec0 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,14 +1,27 @@ TEMPLATE = app -CONFIG += qt debug +CONFIG += qt QT += network script INCLUDEPATH += "../rpc/" "../common/" LIBS += -lprotobuf -win32:LIBS += -L"../common/debug" -lostproto -unix: LIBS += -L"../common" -lostproto -win32:LIBS += -L"../rpc/debug" -lpbrpc -unix:LIBS += -L"../rpc" -lpbrpc -win32:POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" -unix:POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/server/drone.pro b/server/drone.pro index 5fc71d5..b0ae7d2 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,17 +1,30 @@ TEMPLATE = app -CONFIG += qt debug +CONFIG += qt QT += network script DEFINES += HAVE_REMOTE WPCAP INCLUDEPATH += "../rpc" -win32:LIBS += -lwpcap -lpacket -unix:LIBS += -lpcap -win32:LIBS += -L"../common/debug" -lostproto -unix:LIBS += -L"../common" -lostproto -win32:LIBS += -L"../rpc/debug" -lpbrpc -unix:LIBS += -L"../rpc" -lpbrpc +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} LIBS += -lprotobuf -win32:POST_TARGETDEPS += "../common/debug/libostproto.a" "../rpc/debug/libpbrpc.a" -unix:POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui diff --git a/server/myservice.h b/server/myservice.h index 4122c5a..b292151 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -25,7 +25,7 @@ public: const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done); - void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 8486702..96a33b6 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -97,7 +97,13 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, if (handle_ == NULL) goto _open_error; - +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else switch (direction_) { case kDirectionRx: @@ -109,6 +115,7 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, default: Q_ASSERT(false); } +#endif if (ret < 0) goto _set_direction_error; From f64d901729d8dcdbe0684169b6c6c4227e0e3806 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 13 Mar 2010 05:46:24 +0000 Subject: [PATCH 049/294] - Top level Makefile - The default 'all' target no longer has 'qmake' as prerequisite; 'make qmake' shd be done manually on a pristine codebase - eol's changed from dos-style to unix-style - Trivial changes in sample.h/sample.cpp - Implemented ARP protocol --- Makefile | 55 +- client/streamconfigdialog.cpp | 9 +- common/arp.cpp | 925 ++++++++++++++++++++++++++++++++++ common/arp.h | 101 ++++ common/arp.proto | 48 ++ common/arp.ui | 518 +++++++++++++++++++ common/intcombobox.h | 31 ++ common/ostproto.pro | 4 + common/protocolmanager.cpp | 27 +- common/sample.cpp | 2 +- common/sample.h | 6 +- 11 files changed, 1679 insertions(+), 47 deletions(-) create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/intcombobox.h diff --git a/Makefile b/Makefile index 698cf1b..ccbc2b8 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,29 @@ -release: QMAKE_CONFIG=-config release - -all release: qmake - $(MAKE) -C rpc - $(MAKE) -C common - $(MAKE) -C server - $(MAKE) -C client - -clean: - $(MAKE) -C rpc $@ - $(MAKE) -C common $@ - $(MAKE) -C server $@ - $(MAKE) -C client $@ - -distclean: - $(MAKE) -C rpc $@ - $(MAKE) -C common $@ - $(MAKE) -C server $@ - $(MAKE) -C client $@ - -qmake: - cd rpc && qmake $(QMAKE_CONFIG) && cd .. - cd common && qmake $(QMAKE_CONFIG) && cd .. - cd server && qmake $(QMAKE_CONFIG) && cd .. - cd client && qmake $(QMAKE_CONFIG) && cd .. - +release: QMAKE_CONFIG=-config release + +all: + $(MAKE) -C rpc + $(MAKE) -C common + $(MAKE) -C server + $(MAKE) -C client + +release: qmake all + + +clean: + $(MAKE) -C rpc $@ + $(MAKE) -C common $@ + $(MAKE) -C server $@ + $(MAKE) -C client $@ + +distclean: + $(MAKE) -C rpc $@ + $(MAKE) -C common $@ + $(MAKE) -C server $@ + $(MAKE) -C client $@ + +qmake: + cd rpc && qmake $(QMAKE_CONFIG) && cd .. + cd common && qmake $(QMAKE_CONFIG) && cd .. + cd server && qmake $(QMAKE_CONFIG) && cd .. + cd client && qmake $(QMAKE_CONFIG) && cd .. + diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 3c75c3f..61aa7b9 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -123,8 +123,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // TODO(MED): - //! \todo Implement then enable these protocols - ARP, IPv6, ICMP, IGMP - rbL3Arp->setHidden(true); + //! \todo Implement then enable these protocols - IPv6, ICMP, IGMP rbL3Ipv6->setHidden(true); rbL4Icmp->setHidden(true); rbL4Igmp->setHidden(true); @@ -179,7 +178,7 @@ void StreamConfigDialog::setupUiExtra() bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); - bgProto[ProtoL3]->addButton(rbL3Arp, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif @@ -191,8 +190,8 @@ void StreamConfigDialog::setupUiExtra() bgProto[ProtoL4]->addButton(rbL4None, 0); bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); - bgProto[ProtoL4]->addButton(rbL4Icmp, 0xFFFF); - bgProto[ProtoL4]->addButton(rbL4Igmp, 0xFFFF); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); #endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..5fb146b --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,925 @@ +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags |= FieldIsMeta; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + return true; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..6f6c256 --- /dev/null +++ b/common/arp.h @@ -0,0 +1,101 @@ +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..9491a6c --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,48 @@ +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 131; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..44d33c2 --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,31 @@ +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + } + void addItem(int value, const QString &text) + { + QComboBox::addItem(QString("%1 - %2").arg(value).arg(text), value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } +}; diff --git a/common/ostproto.pro b/common/ostproto.pro index b3c37bb..703c4aa 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -11,6 +11,7 @@ FORMS += \ llc.ui \ snap.ui \ vlan.ui \ + arp.ui \ ip4.ui \ tcp.ui \ udp.ui \ @@ -29,6 +30,7 @@ PROTOS += \ vlan.proto \ svlan.proto \ vlanstack.proto \ + arp.proto \ ip4.proto \ tcp.proto \ udp.proto \ @@ -52,6 +54,7 @@ HEADERS += \ vlan.h \ svlan.h \ vlanstack.h \ + arp.h \ ip4.h \ tcp.h \ udp.h \ @@ -71,6 +74,7 @@ SOURCES += \ snap.cpp \ vlan.cpp \ svlan.cpp \ + arp.cpp \ ip4.cpp \ tcp.cpp \ udp.cpp \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index faaef8f..0322344 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -12,6 +12,7 @@ #include "dot2snap.h" #include "vlan.h" #include "vlanstack.h" +#include "arp.h" #include "ip4.h" #include "tcp.h" #include "udp.h" @@ -28,36 +29,38 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kMacFieldNumber, (void*) MacProtocol::createInstance); registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadProtocol::createInstance); + (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, - (void*) Eth2Protocol::createInstance); + (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kDot3FieldNumber, - (void*) Dot3Protocol::createInstance); + (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, - (void*) LlcProtocol::createInstance); + (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, - (void*) SnapProtocol::createInstance); + (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kSvlanFieldNumber, - (void*) SVlanProtocol::createInstance); + (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, - (void*) VlanProtocol::createInstance); + (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackProtocol::createInstance); + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, - (void*) Ip4Protocol::createInstance); + (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, - (void*) TcpProtocol::createInstance); + (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, - (void*) UdpProtocol::createInstance); + (void*) UdpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, - (void*) UserScriptProtocol::createInstance); + (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, - (void*) SampleProtocol::createInstance); + (void*) SampleProtocol::createInstance); populateNeighbourProtocols(); } diff --git a/common/sample.cpp b/common/sample.cpp index 0b7da64..f44b979 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -84,7 +84,7 @@ quint32 SampleProtocol::protocolId(ProtocolIdType type) const return AbstractProtocol::protocolId(type); } -int SampleProtocol::fieldCount() const +int SampleProtocol::fieldCount() const { return sample_fieldCount; } diff --git a/common/sample.h b/common/sample.h index 55f549e..443d8c3 100644 --- a/common/sample.h +++ b/common/sample.h @@ -1,11 +1,11 @@ #ifndef _SAMPLE_H #define _SAMPLE_H -#include "abstractprotocol.h" - #include "sample.pb.h" #include "ui_sample.h" +#include "abstractprotocol.h" + /* Sample Protocol Frame Format - +-----+------+------+------+------+------+ @@ -61,7 +61,7 @@ public: virtual QString name() const; virtual QString shortName() const; - virtual int fieldCount() const; + virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, From 4dc3d2d7f90d3d10c8593524866be4df74739a4f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 17 Mar 2010 15:47:56 +0000 Subject: [PATCH 050/294] - Added TODO instruction comments to the sample protocol - Implemented ICMP protocol builder - Added #ifdefs to intcombobox.h to prevent multiple inclusion --- client/streamconfigdialog.cpp | 3 +- common/icmp.cpp | 400 ++++++++++++++++++++++++++++++++++ common/icmp.h | 75 +++++++ common/icmp.proto | 19 ++ common/icmp.ui | 144 ++++++++++++ common/intcombobox.h | 5 + common/ostproto.pro | 4 + common/protocolmanager.cpp | 3 + common/sample.cpp | 48 +++- 9 files changed, 689 insertions(+), 12 deletions(-) create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 61aa7b9..41c9732 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -123,9 +123,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // TODO(MED): - //! \todo Implement then enable these protocols - IPv6, ICMP, IGMP + //! \todo Implement then enable these protocols - IPv6, IGMP rbL3Ipv6->setHidden(true); - rbL4Icmp->setHidden(true); rbL4Igmp->setHidden(true); //! \todo Enable navigation of streams pbPrev->setDisabled(true); diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..32d0fb5 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,400 @@ +#include + +#include "icmp.h" + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + typeCombo->addItem(0, "Echo Reply"); + typeCombo->addItem(3, "Destination Unreachable"); + typeCombo->addItem(4, "Source Quench"); + typeCombo->addItem(5, "Redirect"); + typeCombo->addItem(8, "Echo Request"); + typeCombo->addItem(11, "Time Exceeded"); + typeCombo->addItem(12, "Parameter Problem"); + typeCombo->addItem(13, "Timestamp Request"); + typeCombo->addItem(14, "Timestamp Reply"); + typeCombo->addItem(17, "Address Mask Request"); + typeCombo->addItem(18, "Address Mask Reply"); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x1; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= FieldIsCksum; + break; + + case icmp_identifier: + case icmp_sequence: + break; + + case icmp_is_override_checksum: + flags |= FieldIsMeta; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + printf("%s: attrib = %d, cksum = %d\n", __FUNCTION__, attrib, cksum); + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..3a9fba6 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,75 @@ +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +/* +Icmp Protocol Frame Format - + +-----+------+------+-----+-----+ + | TYP | CODE | CSUM | ID | SEQ | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+-----+-----+ +Figures in brackets represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + IcmpConfigForm(QWidget *parent = 0); +private slots: +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_identifier, + icmp_sequence, + + // Meta Fields + icmp_is_override_checksum, + + icmp_fieldCount + }; + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..72dcd38 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,19 @@ +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + optional bool is_override_checksum = 1; + + optional uint32 type = 2 [default = 0x8]; // echo request + optional uint32 code = 3; + optional uint32 checksum = 4; + optional uint32 identifier = 5 [default = 1234]; + optional uint32 sequence = 6; +} + +extend Protocol { + optional Icmp icmp = 142; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..bf54421 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,144 @@ + + Icmp + + + + 0 + 0 + 258 + 168 + + + + Form + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Checksum + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
diff --git a/common/intcombobox.h b/common/intcombobox.h index 44d33c2..ca62511 100644 --- a/common/intcombobox.h +++ b/common/intcombobox.h @@ -1,3 +1,6 @@ +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + #include class IntComboBox : public QComboBox @@ -29,3 +32,5 @@ public: setEditText(QString().setNum(value)); } }; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 703c4aa..e2b9cb3 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -13,6 +13,7 @@ FORMS += \ vlan.ui \ arp.ui \ ip4.ui \ + icmp.ui \ tcp.ui \ udp.ui \ userscript.ui \ @@ -32,6 +33,7 @@ PROTOS += \ vlanstack.proto \ arp.proto \ ip4.proto \ + icmp.proto \ tcp.proto \ udp.proto \ userscript.proto \ @@ -56,6 +58,7 @@ HEADERS += \ vlanstack.h \ arp.h \ ip4.h \ + icmp.h \ tcp.h \ udp.h \ userscript.h \ @@ -76,6 +79,7 @@ SOURCES += \ svlan.cpp \ arp.cpp \ ip4.cpp \ + icmp.cpp \ tcp.cpp \ udp.cpp \ userscript.cpp \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 0322344..f65c9b2 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -14,6 +14,7 @@ #include "vlanstack.h" #include "arp.h" #include "ip4.h" +#include "icmp.h" #include "tcp.h" #include "udp.h" #include "userscript.h" @@ -52,6 +53,8 @@ ProtocolManager::ProtocolManager() (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, diff --git a/common/sample.cpp b/common/sample.cpp index f44b979..a37ba49 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -54,7 +54,7 @@ QString SampleProtocol::shortName() const } /*! - Return the ProtocolIdType for your protocol \n + TODO Return the ProtocolIdType for your protocol \n If your protocol doesn't have a protocolId field, you don't need to reimplement this method - the base class implementation will do the @@ -66,7 +66,7 @@ AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const } /*! - Return the protocolId for your protoocol based on the 'type' requested \n + TODO Return the protocolId for your protoocol based on the 'type' requested \n If not all types are valid for your protocol, handle the valid type(s) and for the remaining fallback to the base class implementation; if your @@ -89,6 +89,11 @@ int SampleProtocol::fieldCount() const return sample_fieldCount; } +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; @@ -123,6 +128,11 @@ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const return flags; } +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -213,12 +223,11 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: case FieldFrameValue: case FieldTextValue: - { if (data.is_override_checksum()) cksum = data.checksum(); else cksum = protocolFrameCksum(streamIndex, CksumIp); - } + break; default: cksum = 0; // avoid the 'maybe used unitialized' warning break; @@ -257,7 +266,9 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return data.x(); case FieldTextValue: + // Use the following line for display in decimal return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); case FieldFrameValue: { @@ -280,13 +291,15 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return data.y(); case FieldTextValue: + // Use the following line for display in decimal //return QString("%1").arg(data.y()); - return QString("%1").arg(data.y(), 8, BASE_HEX, QChar('0')); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; - fv.resize(4); - qToBigEndian((quint32) data.y(), (uchar*) fv.data()); + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); return fv; } default: @@ -317,6 +330,11 @@ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ bool SampleProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { @@ -387,7 +405,7 @@ _exit: } /*! - Return the protocol frame size in bytes\n + TODO: Return the protocol frame size in bytes\n If your protocol has a fixed size - you don't need to reimplement this; the base class implementation is good enough @@ -398,7 +416,7 @@ int SampleProtocol::protocolFrameSize(int streamIndex) const } /*! - If your protocol has any variable fields, return true \n + TODO: If your protocol has any variable fields, return true \n Otherwise you don't need to reimplement this method - the base class always returns false @@ -409,7 +427,7 @@ bool SampleProtocol::isProtocolFrameValueVariable() const } /*! - If your protocol frame size can vary across pkts of the same stream, + TODO: If your protocol frame size can vary across pkts of the same stream, return true \n Otherwise you don't need to reimplement this method - the base class always @@ -431,6 +449,11 @@ QWidget* SampleProtocol::configWidget() return configForm; } +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ void SampleProtocol::loadConfigWidget() { configWidget(); @@ -451,6 +474,11 @@ void SampleProtocol::loadConfigWidget() } +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ void SampleProtocol::storeConfigWidget() { bool isOk; From cbf114c29da28eea888acddcfab775672020d7f5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 24 Mar 2010 15:56:11 +0000 Subject: [PATCH 051/294] - Added version/revision info to both client and server UI - Top level 'make clean' and 'make distclean' now do not stop in case of errors - 'make distclean' now removes the object_script.* generated files - Added the Logo to the About dialog and also the application icon --- Makefile | 16 ++--- client/about.ui | 155 +++++++++++++++++++++++++++--------------- client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/mainwindow.cpp | 5 ++ client/ostinato.pro | 6 ++ client/ostinato.qrc | 2 + client/ostinato.rc | 1 + common/ostproto.pro | 2 + server/drone.cpp | 4 ++ server/drone.pro | 3 + server/drone.ui | 10 +++ version.pri | 16 +++++ 14 files changed, 159 insertions(+), 61 deletions(-) create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/name.png create mode 100644 client/ostinato.rc create mode 100644 version.pri diff --git a/Makefile b/Makefile index ccbc2b8..557a682 100644 --- a/Makefile +++ b/Makefile @@ -10,16 +10,16 @@ release: qmake all clean: - $(MAKE) -C rpc $@ - $(MAKE) -C common $@ - $(MAKE) -C server $@ - $(MAKE) -C client $@ + -$(MAKE) -C client $@ + -$(MAKE) -C server $@ + -$(MAKE) -C common $@ + -$(MAKE) -C rpc $@ distclean: - $(MAKE) -C rpc $@ - $(MAKE) -C common $@ - $(MAKE) -C server $@ - $(MAKE) -C client $@ + -$(MAKE) -C client $@ + -$(MAKE) -C server $@ + -$(MAKE) -C common $@ + -$(MAKE) -C rpc $@ qmake: cd rpc && qmake $(QMAKE_CONFIG) && cd .. diff --git a/client/about.ui b/client/about.ui index 6ce0aed..5dab03e 100644 --- a/client/about.ui +++ b/client/about.ui @@ -5,10 +5,16 @@ 0 0 - 400 - 300 + 456 + 303 + + + 0 + 0 + + About @@ -19,62 +25,103 @@ QFrame::Box - QFrame::Raised + QFrame::Sunken - - - Qt::Vertical - - - - 360 - 51 - - - + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2010 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + - + - <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> - - - Qt::AlignCenter - - - - - - - Copyright (c) 2007-2010 Srivats P. - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) Qt::AlignCenter @@ -96,7 +143,9 @@ p, li { white-space: pre-wrap; } - + + + buttonBox diff --git a/client/icons/logo.ico b/client/icons/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..28f1f06cad620b6892b31cb1a0f9178a960c1c5b GIT binary patch literal 2238 zcmeHI(NP;Q5PfH%AQUIE6PZhnTzo!pn*2crm=2%<=m4kyN`MaFFMmJ<;0k~aC;=J( zo^-Nd!VLMzOnz98tkdq(?%OBb9FXx>Rp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YUGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsy #include +extern const char* version; +extern const char* revision; + PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) @@ -58,6 +61,8 @@ void MainWindow::on_actionHelpAbout_triggered() Ui::About about; about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); aboutDialog->exec(); diff --git a/client/ostinato.pro b/client/ostinato.pro index c24cec0..2738df9 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,5 +1,6 @@ TEMPLATE = app CONFIG += qt +RC_FILE = ostinato.rc QT += network script INCLUDEPATH += "../rpc/" "../common/" LIBS += -lprotobuf @@ -67,5 +68,10 @@ SOURCES += \ streamlistdelegate.cpp \ streammodel.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../version.pri) + # TODO(LOW): Test only include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 5f4df48..cb71e4c 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -14,7 +14,9 @@ icons/control_stop.png icons/deco_exclusive.png icons/delete.png + icons/logo.png icons/magnifier.png + icons/name.png icons/portgroup_add.png icons/portgroup_connect.png icons/portgroup_delete.png diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/common/ostproto.pro b/common/ostproto.pro index e2b9cb3..2c8aaf3 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -99,3 +99,5 @@ protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h protobuf_impl.commands = $$escape_expand(\n) protobuf_impl.variable_out = GENERATED_SOURCES QMAKE_EXTRA_COMPILERS += protobuf_impl + +QMAKE_DISTCLEAN += object_script.* diff --git a/server/drone.cpp b/server/drone.cpp index 0e03b29..7f84033 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -7,11 +7,15 @@ #include extern int myport; +extern const char* version; +extern const char* revision; Drone::Drone(QWidget *parent) : QWidget(parent) { setupUi(this); + versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); rpcServer = new RpcServer(); service = new MyService(); diff --git a/server/drone.pro b/server/drone.pro index b0ae7d2..34ea026 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -38,3 +38,6 @@ SOURCES += \ SOURCES += myservice.cpp SOURCES += pcapextra.cpp +QMAKE_DISTCLEAN += object_script.* + +include (../version.pri) diff --git a/server/drone.ui b/server/drone.ui index 73fde04..e2e0613 100644 --- a/server/drone.ui +++ b/server/drone.ui @@ -42,6 +42,16 @@ p, li { white-space: pre-wrap; }
+ + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..df1335c --- /dev/null +++ b/version.pri @@ -0,0 +1,16 @@ +APP_VERSION = 0.1 +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$(shell svnversion .)\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$(shell svnversion .)\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From 602be86a42bc19767d2ec75404a7d24725554b9c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 27 Mar 2010 14:27:55 +0000 Subject: [PATCH 052/294] Fixes - Win32: If bindconfig.exe is not present, WinPcapPort::hasExclusiveControl() returns false instead of true (we assume we don't have exclusive control) - Win32: WinPcapPort now looks for bindconfig.exe in the app's dirPath() instead of the current directory --- server/winpcapport.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index b9ddfdf..2e23aac 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -1,5 +1,6 @@ #include "winpcapport.h" +#include #include #ifdef Q_OS_WIN32 @@ -58,11 +59,16 @@ OstProto::LinkState WinPcapPort::linkState() bool WinPcapPort::hasExclusiveControl() { QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); int exitCode; qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); - exitCode = QProcess::execute("bindconfig.exe", + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, QStringList() << "comp" << portName); qDebug("%s: exit code %d", __FUNCTION__, exitCode); @@ -76,13 +82,18 @@ bool WinPcapPort::hasExclusiveControl() bool WinPcapPort::setExclusiveControl(bool exclusive) { QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); QString status; qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + if (!QFile::exists(bindConfigFilePath)) + return false; + status = exclusive ? "disable" : "enable"; - QProcess::execute("bindconfig.exe", + QProcess::execute(bindConfigFilePath, QStringList() << "comp" << portName << status); updateNotes(); From 59c9ae8baa3645bc2fee54abcbc433d64f6d64fa Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 27 Mar 2010 18:38:57 +0000 Subject: [PATCH 053/294] - Added Copyright and License (GPLv3) notifications to all source files viz. *.h, *.cpp, *.proto - Also added a "License" tab to the 'About Ostinato' dialog box. --- client/about.ui | 245 +++++++++++++++++-------------- client/dumpview.cpp | 19 +++ client/dumpview.h | 19 +++ client/hexlineedit.cpp | 19 +++ client/hexlineedit.h | 19 +++ client/main.cpp | 19 +++ client/mainwindow.cpp | 19 +++ client/mainwindow.h | 19 +++ client/modeltest.cpp | 19 +++ client/modeltest.h | 19 +++ client/packetmodel.cpp | 19 +++ client/packetmodel.h | 19 +++ client/port.cpp | 19 +++ client/port.h | 19 +++ client/portgroup.cpp | 19 +++ client/portgroup.h | 19 +++ client/portgrouplist.cpp | 19 +++ client/portgrouplist.h | 19 +++ client/portmodel.cpp | 19 +++ client/portmodel.h | 19 +++ client/portstatsfilterdialog.cpp | 19 +++ client/portstatsfilterdialog.h | 19 +++ client/portstatsmodel.cpp | 19 +++ client/portstatsmodel.h | 19 +++ client/portstatswindow.cpp | 19 +++ client/portstatswindow.h | 19 +++ client/portswindow.cpp | 19 +++ client/portswindow.h | 19 +++ client/stream.cpp | 19 +++ client/stream.h | 19 +++ client/streamconfigdialog.cpp | 19 +++ client/streamconfigdialog.h | 19 +++ client/streamlistdelegate.cpp | 19 +++ client/streamlistdelegate.h | 19 +++ client/streammodel.cpp | 19 +++ client/streammodel.h | 19 +++ common/abstractprotocol.cpp | 19 +++ common/abstractprotocol.h | 19 +++ common/arp.cpp | 19 +++ common/arp.h | 19 +++ common/arp.proto | 19 +++ common/comboprotocol.h | 19 +++ common/dot2llc.h | 19 +++ common/dot2llc.proto | 19 +++ common/dot2snap.h | 19 +++ common/dot2snap.proto | 19 +++ common/dot3.cpp | 19 +++ common/dot3.h | 19 +++ common/dot3.proto | 19 +++ common/eth2.cpp | 19 +++ common/eth2.h | 19 +++ common/eth2.proto | 19 +++ common/icmp.cpp | 19 +++ common/icmp.h | 19 +++ common/icmp.proto | 19 +++ common/intcombobox.h | 19 +++ common/ip4.cpp | 19 +++ common/ip4.h | 19 +++ common/ip4.proto | 19 +++ common/llc.cpp | 19 +++ common/llc.h | 19 +++ common/llc.proto | 19 +++ common/mac.cpp | 19 +++ common/mac.h | 19 +++ common/mac.proto | 19 +++ common/payload.cpp | 19 +++ common/payload.h | 19 +++ common/payload.proto | 19 +++ common/protocol.proto | 19 +++ common/protocollist.cpp | 19 +++ common/protocollist.h | 19 +++ common/protocollistiterator.cpp | 19 +++ common/protocollistiterator.h | 19 +++ common/protocolmanager.cpp | 19 +++ common/protocolmanager.h | 19 +++ common/sample.cpp | 19 +++ common/sample.h | 19 +++ common/sample.proto | 19 +++ common/snap.cpp | 19 +++ common/snap.h | 19 +++ common/snap.proto | 19 +++ common/streambase.cpp | 19 +++ common/streambase.h | 19 +++ common/svlan.cpp | 19 +++ common/svlan.h | 19 +++ common/svlan.proto | 19 +++ common/tcp.cpp | 19 +++ common/tcp.h | 19 +++ common/tcp.proto | 19 +++ common/udp.cpp | 19 +++ common/udp.h | 19 +++ common/udp.proto | 19 +++ common/userscript.cpp | 19 +++ common/userscript.h | 19 +++ common/userscript.proto | 19 +++ common/vlan.cpp | 19 +++ common/vlan.h | 19 +++ common/vlan.proto | 19 +++ common/vlanstack.h | 19 +++ common/vlanstack.proto | 19 +++ rpc/pbhelper.h | 19 +++ rpc/pbrpcchannel.cpp | 19 +++ rpc/pbrpcchannel.h | 19 +++ rpc/pbrpccommon.h | 19 +++ rpc/pbrpccontroller.h | 19 +++ rpc/rpcserver.cpp | 19 +++ rpc/rpcserver.h | 19 +++ server/abstractport.cpp | 19 +++ server/abstractport.h | 19 +++ server/drone.cpp | 19 +++ server/drone.h | 19 +++ server/drone_main.cpp | 19 +++ server/myservice.cpp | 19 +++ server/myservice.h | 19 +++ server/pcapextra.cpp | 19 +++ server/pcapextra.h | 19 +++ server/pcapport.cpp | 19 +++ server/pcapport.h | 19 +++ server/portmanager.cpp | 19 +++ server/portmanager.h | 19 +++ server/winpcapport.cpp | 19 +++ server/winpcapport.h | 19 +++ 122 files changed, 2434 insertions(+), 110 deletions(-) diff --git a/client/about.ui b/client/about.ui index 5dab03e..34cd66c 100644 --- a/client/about.ui +++ b/client/about.ui @@ -5,8 +5,8 @@ 0 0 - 456 - 303 + 500 + 327 @@ -16,119 +16,144 @@ - About + About Ostinato - - - QFrame::Box + + + 0 - - QFrame::Sunken - - - - - - - - - 0 - 0 - - - - - - - :/icons/logo.png - - - false - - - Qt::AlignCenter - - - - - - - - - Qt::Vertical - - - - 20 - 21 - - - - - - - - - - - :/icons/name.png - - - Qt::AlignCenter - - - - - - - Version/Revision Placeholder - - - Qt::AlignCenter - - - - - - - Copyright (c) 2007-2010 Srivats P. - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 21 - - - - - - - - - - - - Logo (c): Dhiman Sengupta + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2010 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) - - - Qt::AlignCenter - - - - + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp index 65584ee..fe99e01 100644 --- a/client/dumpview.cpp +++ b/client/dumpview.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "dumpview.h" //! \todo Enable Scrollbars diff --git a/client/dumpview.h b/client/dumpview.h index 6f7db2e..b170cd0 100644 --- a/client/dumpview.h +++ b/client/dumpview.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #include // FIXME: High diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp index 68d7dab..6150084 100644 --- a/client/hexlineedit.cpp +++ b/client/hexlineedit.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "hexlineedit.h" #include "qdebug.h" diff --git a/client/hexlineedit.h b/client/hexlineedit.h index 09f638a..20ad460 100644 --- a/client/hexlineedit.h +++ b/client/hexlineedit.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _HEXLINEEDIT #define _HEXLINEEDIT diff --git a/client/main.cpp b/client/main.cpp index 53b43a6..e927b60 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "mainwindow.h" #include diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 2cb07ae..2bd7655 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "mainwindow.h" #if 0 diff --git a/client/mainwindow.h b/client/mainwindow.h index 9ddd926..0d8f02f 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _MAIN_WINDOW_H #define _MAIN_WINDOW_H diff --git a/client/modeltest.cpp b/client/modeltest.cpp index 2598c58..4b3de1f 100644 --- a/client/modeltest.cpp +++ b/client/modeltest.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. diff --git a/client/modeltest.h b/client/modeltest.h index 38b6b2b..9b2d39f 100644 --- a/client/modeltest.h +++ b/client/modeltest.h @@ -1,3 +1,22 @@ +/* +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 +*/ + /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 11ed59d..b095c45 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "packetmodel.h" diff --git a/client/packetmodel.h b/client/packetmodel.h index 4f3b542..08dcea9 100644 --- a/client/packetmodel.h +++ b/client/packetmodel.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PACKET_MODEL_H #define _PACKET_MODEL_H diff --git a/client/port.cpp b/client/port.cpp index 565af93..eda09f6 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/client/port.h b/client/port.h index 6e2e9d3..91f6d8a 100644 --- a/client/port.h +++ b/client/port.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_H #define _PORT_H diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 7531ef6..c5a8a2b 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portgroup.h" #include diff --git a/client/portgroup.h b/client/portgroup.h index 88e96f7..dbf831c 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_GROUP_H #define _PORT_GROUP_H diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 6d8a079..3d6a8be 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portgrouplist.h" // TODO(LOW): Remove diff --git a/client/portgrouplist.h b/client/portgrouplist.h index e8d2433..3083c26 100644 --- a/client/portgrouplist.h +++ b/client/portgrouplist.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_GROUP_LIST_H #define _PORT_GROUP_LIST_H diff --git a/client/portmodel.cpp b/client/portmodel.cpp index cb62664..1d13eda 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portmodel.h" #include "portgrouplist.h" diff --git a/client/portmodel.h b/client/portmodel.h index 566785c..2027f0b 100644 --- a/client/portmodel.h +++ b/client/portmodel.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_MODEL_H #define _PORT_MODEL_H diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 724ee40..457a7c8 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portstatsfilterdialog.h" PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h index 28d94ed..7eea255 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_STATS_FILTER_DIALOG_H #define _PORT_STATS_FILTER_DIALOG_H diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 80241cf..5bfd33e 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portstatsmodel.h" #include "portgrouplist.h" diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index 0a2e3aa..d50508b 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_STATS_MODEL_H #define _PORT_STATS_MODEL_H diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 9b8b224..035a23c 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portstatswindow.h" #include "portstatsmodel.h" diff --git a/client/portstatswindow.h b/client/portstatswindow.h index b2ad6f2..39f9108 100644 --- a/client/portstatswindow.h +++ b/client/portstatswindow.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORT_STATS_WINDOW_H #define _PORT_STATS_WINDOW_H diff --git a/client/portswindow.cpp b/client/portswindow.cpp index f3b4760..0ef8a15 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portswindow.h" #include diff --git a/client/portswindow.h b/client/portswindow.h index 6389887..607c40a 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PORTS_WINDOW_H #define _PORTS_WINDOW_H diff --git a/client/stream.cpp b/client/stream.cpp index 7d21b14..a24c971 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/client/stream.h b/client/stream.h index de7508d..213af70 100644 --- a/client/stream.h +++ b/client/stream.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _STREAM_H #define _STREAM_H diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 41c9732..dd02697 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "streamconfigdialog.h" diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 0961767..637bc9c 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _STREAM_CONFIG_DIALOG_H #define _STREAM_CONFIG_DIALOG_H diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp index f701c13..f15bc18 100644 --- a/client/streamlistdelegate.cpp +++ b/client/streamlistdelegate.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include #include diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h index 6a52aaa..a98a34e 100644 --- a/client/streamlistdelegate.h +++ b/client/streamlistdelegate.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef STREAM_LIST_DELEGATE_H #define STREAM_LIST_DELEGATE_H diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 76735d0..5c57cdb 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "stream.h" #include "streammodel.h" #include "portgrouplist.h" diff --git a/client/streammodel.h b/client/streammodel.h index e73bb18..b0b41d2 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _STREAM_MODEL_H #define _STREAM_MODEL_H diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index f66dfdd..750a26b 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "abstractprotocol.h" diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index fb6a703..9d12dd2 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _ABSTRACT_PROTOCOL_H #define _ABSTRACT_PROTOCOL_H diff --git a/common/arp.cpp b/common/arp.cpp index 5fb146b..7548c62 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/arp.h b/common/arp.h index 6f6c256..677b73a 100644 --- a/common/arp.h +++ b/common/arp.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _ARP_H #define _ARP_H diff --git a/common/arp.proto b/common/arp.proto index 9491a6c..9a4bd52 100644 --- a/common/arp.proto +++ b/common/arp.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/comboprotocol.h b/common/comboprotocol.h index 9d10348..c6efa0a 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _COMBO_PROTOCOL_H #define _COMBO_PROTOCOL_H diff --git a/common/dot2llc.h b/common/dot2llc.h index 44f6a3b..b858914 100644 --- a/common/dot2llc.h +++ b/common/dot2llc.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _DOT2_LLC_H #define _DOT2_LLC_H diff --git a/common/dot2llc.proto b/common/dot2llc.proto index 51eb756..08e857c 100644 --- a/common/dot2llc.proto +++ b/common/dot2llc.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; import "dot3.proto"; import "llc.proto"; diff --git a/common/dot2snap.h b/common/dot2snap.h index 2ad3ead..0da586a 100644 --- a/common/dot2snap.h +++ b/common/dot2snap.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _DOT2_SNAP_H #define _DOT2_SNAP_H diff --git a/common/dot2snap.proto b/common/dot2snap.proto index ce41b8f..3af4261 100644 --- a/common/dot2snap.proto +++ b/common/dot2snap.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/dot3.cpp b/common/dot3.cpp index 1c16f40..adbe5d5 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/dot3.h b/common/dot3.h index 925374a..8a0d5c2 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _DOT3_H #define _DOT3_H diff --git a/common/dot3.proto b/common/dot3.proto index 154f554..e3f4344 100644 --- a/common/dot3.proto +++ b/common/dot3.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/eth2.cpp b/common/eth2.cpp index 2939fed..dd85063 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/eth2.h b/common/eth2.h index cae1bb3..dcb8a7a 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _ETH2_H #define _ETH2_H diff --git a/common/eth2.proto b/common/eth2.proto index c5339a9..e0c52a2 100644 --- a/common/eth2.proto +++ b/common/eth2.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/icmp.cpp b/common/icmp.cpp index 32d0fb5..0c03700 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "icmp.h" diff --git a/common/icmp.h b/common/icmp.h index 3a9fba6..0d6f218 100644 --- a/common/icmp.h +++ b/common/icmp.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _ICMP_H #define _ICMP_H diff --git a/common/icmp.proto b/common/icmp.proto index 72dcd38..f48e9a7 100644 --- a/common/icmp.proto +++ b/common/icmp.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/intcombobox.h b/common/intcombobox.h index ca62511..7da3351 100644 --- a/common/intcombobox.h +++ b/common/intcombobox.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef __INT_COMBO_BOX #define __INT_COMBO_BOX diff --git a/common/ip4.cpp b/common/ip4.cpp index c2594f3..f3bb887 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/ip4.h b/common/ip4.h index a9a40b9..ab7b3de 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _IPV4_H #define _IPV4_H diff --git a/common/ip4.proto b/common/ip4.proto index ffdfcae..9914977 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/llc.cpp b/common/llc.cpp index 5a5f1d2..76e8e46 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/llc.h b/common/llc.h index 5de0b72..2cc9fe8 100644 --- a/common/llc.h +++ b/common/llc.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _LLC_H #define _LLC_H diff --git a/common/llc.proto b/common/llc.proto index c1966ab..1bb6324 100644 --- a/common/llc.proto +++ b/common/llc.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/mac.cpp b/common/mac.cpp index 30f0ec2..706931a 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/mac.h b/common/mac.h index 242ce2a..48d0e35 100644 --- a/common/mac.h +++ b/common/mac.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _MAC_H #define _MAC_H diff --git a/common/mac.proto b/common/mac.proto index 642f725..475590d 100644 --- a/common/mac.proto +++ b/common/mac.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/payload.cpp b/common/payload.cpp index 292c0c5..2c6c267 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/payload.h b/common/payload.h index d468a16..2fd32d2 100644 --- a/common/payload.h +++ b/common/payload.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PAYLOAD_H #define _PAYLOAD_H diff --git a/common/payload.proto b/common/payload.proto index 0cbc878..02cfc04 100644 --- a/common/payload.proto +++ b/common/payload.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/protocol.proto b/common/protocol.proto index d1205f7..4bd5899 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + package OstProto; message StreamId { diff --git a/common/protocollist.cpp b/common/protocollist.cpp index 08ff40a..1b3397c 100644 --- a/common/protocollist.cpp +++ b/common/protocollist.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "protocollist.h" #include "abstractprotocol.h" diff --git a/common/protocollist.h b/common/protocollist.h index d760f14..62df3c9 100644 --- a/common/protocollist.h +++ b/common/protocollist.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #include class AbstractProtocol; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp index 242d8d9..a081365 100644 --- a/common/protocollistiterator.cpp +++ b/common/protocollistiterator.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "protocollistiterator.h" #include "protocollist.h" #include "abstractprotocol.h" diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h index 463dae9..6baa39f 100644 --- a/common/protocollistiterator.h +++ b/common/protocollistiterator.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #include class AbstractProtocol; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index f65c9b2..130e5ce 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "protocolmanager.h" #include "abstractprotocol.h" diff --git a/common/protocolmanager.h b/common/protocolmanager.h index c80315e..ac64919 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PROTOCOL_MANAGER_H #define _PROTOCOL_MANAGER_H diff --git a/common/sample.cpp b/common/sample.cpp index a37ba49..4d601bf 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "sample.h" diff --git a/common/sample.h b/common/sample.h index 443d8c3..b92152b 100644 --- a/common/sample.h +++ b/common/sample.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SAMPLE_H #define _SAMPLE_H diff --git a/common/sample.proto b/common/sample.proto index 16d4daf..6d74304 100644 --- a/common/sample.proto +++ b/common/sample.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/snap.cpp b/common/snap.cpp index cfabc1b..6803253 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/snap.h b/common/snap.h index 42eae0c..2e60b84 100644 --- a/common/snap.h +++ b/common/snap.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SNAP_H #define _SNAP_H diff --git a/common/snap.proto b/common/snap.proto index 7ac3254..bf54803 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/streambase.cpp b/common/streambase.cpp index a09093b..109252c 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "streambase.h" #include "abstractprotocol.h" #include "protocollist.h" diff --git a/common/streambase.h b/common/streambase.h index 5b40b1d..61964c6 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _STREAM_BASE_H #define _STREAM_BASE_H diff --git a/common/svlan.cpp b/common/svlan.cpp index 6d30cfc..893671d 100644 --- a/common/svlan.cpp +++ b/common/svlan.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "svlan.h" diff --git a/common/svlan.h b/common/svlan.h index 3c7d673..7ba051b 100644 --- a/common/svlan.h +++ b/common/svlan.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SVLAN_H #define _SVLAN_H diff --git a/common/svlan.proto b/common/svlan.proto index 42f45bc..aa02f70 100644 --- a/common/svlan.proto +++ b/common/svlan.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; import "vlan.proto"; diff --git a/common/tcp.cpp b/common/tcp.cpp index 7a66347..9c44ec0 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/tcp.h b/common/tcp.h index 8ef7ebf..3b32b35 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _TCP_H #define _TCP_H diff --git a/common/tcp.proto b/common/tcp.proto index 05dd181..39d96be 100644 --- a/common/tcp.proto +++ b/common/tcp.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/udp.cpp b/common/udp.cpp index b755310..7bb74e3 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include diff --git a/common/udp.h b/common/udp.h index 652c03a..86c2ca8 100644 --- a/common/udp.h +++ b/common/udp.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _UDP_H #define _UDP_H diff --git a/common/udp.proto b/common/udp.proto index 23ed43e..5c30eac 100644 --- a/common/udp.proto +++ b/common/udp.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/userscript.cpp b/common/userscript.cpp index ba26c72..fa084f1 100644 --- a/common/userscript.cpp +++ b/common/userscript.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "userscript.h" #include diff --git a/common/userscript.h b/common/userscript.h index 3f51142..4ef0d91 100644 --- a/common/userscript.h +++ b/common/userscript.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _USER_SCRIPT_H #define _USER_SCRIPT_H diff --git a/common/userscript.proto b/common/userscript.proto index aaf2621..484223f 100644 --- a/common/userscript.proto +++ b/common/userscript.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/vlan.cpp b/common/vlan.cpp index 5abee7d..2946068 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include #include "vlan.h" diff --git a/common/vlan.h b/common/vlan.h index f276ce5..728572b 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _Vlan_H #define _Vlan_H diff --git a/common/vlan.proto b/common/vlan.proto index a963444..802627c 100644 --- a/common/vlan.proto +++ b/common/vlan.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/common/vlanstack.h b/common/vlanstack.h index 4b24464..847ac3d 100644 --- a/common/vlanstack.h +++ b/common/vlanstack.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _VLAN_STACK_H #define _VLAN_STACK_H diff --git a/common/vlanstack.proto b/common/vlanstack.proto index 4e6e9a0..203f3ef 100644 --- a/common/vlanstack.proto +++ b/common/vlanstack.proto @@ -1,3 +1,22 @@ +/* +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 +*/ + import "protocol.proto"; package OstProto; diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h index e87d4ca..7ab80b3 100644 --- a/rpc/pbhelper.h +++ b/rpc/pbhelper.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PB_HELPER_H #define _PB_HELPER_H diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 9b5997f..521aca5 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "pbrpcchannel.h" PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h index 8c2efbc..682c553 100644 --- a/rpc/pbrpcchannel.h +++ b/rpc/pbrpcchannel.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PB_RPC_CHANNEL_H #define _PB_RPC_CHANNEL_H diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index 12040ee..5cbbb57 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PB_RPC_COMMON_H #define _PB_RPC_COMMON_H diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 50260d7..fa11cdd 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PB_RPC_CONTROLLER_H #define _PB_RPC_CONTROLLER_H diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 36d96e9..5d67833 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + //#include "pbhelper.h" #include "rpcserver.h" diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 0872373..1e9c93e 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _RPC_SERVER_H #define _RPC_SERVER_H diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 7d2c0cf..70d833f 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "abstractport.h" #include diff --git a/server/abstractport.h b/server/abstractport.h index ba13d25..4b036fa 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SERVER_ABSTRACT_PORT_H #define _SERVER_ABSTRACT_PORT_H diff --git a/server/drone.cpp b/server/drone.cpp index 7f84033..8206ac2 100644 --- a/server/drone.cpp +++ b/server/drone.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "drone.h" #include "rpcserver.h" diff --git a/server/drone.h b/server/drone.h index c8d9ff2..7466a76 100644 --- a/server/drone.h +++ b/server/drone.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _DRONE_H #define _DRONE_H diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 0bac7c1..722f5c0 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "drone.h" int myport; diff --git a/server/myservice.cpp b/server/myservice.cpp index 3699cfd..424a95a 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "myservice.h" diff --git a/server/myservice.h b/server/myservice.h index b292151..09cb479 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _MY_SERVICE_H #define _MY_SERVICE_H diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp index b4fdba7..4acbda9 100644 --- a/server/pcapextra.cpp +++ b/server/pcapextra.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "pcapextra.h" #include // memcpy() diff --git a/server/pcapextra.h b/server/pcapextra.h index de4cec1..415fe3e 100644 --- a/server/pcapextra.h +++ b/server/pcapextra.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _PCAP_EXTRA_H #define _PCAP_EXTRA_H diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 96a33b6..f9412cf 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "pcapport.h" #include diff --git a/server/pcapport.h b/server/pcapport.h index 5a1ef3f..0001b0a 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SERVER_PCAP_PORT_H #define _SERVER_PCAP_PORT_H diff --git a/server/portmanager.cpp b/server/portmanager.cpp index bec541f..b4f7572 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "portmanager.h" #include diff --git a/server/portmanager.h b/server/portmanager.h index 1d4bd73..2407bf2 100644 --- a/server/portmanager.h +++ b/server/portmanager.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SERVER_PORT_MANAGER_H #define _SERVER_PORT_MANAGER_H diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 2e23aac..3f38a15 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -1,3 +1,22 @@ +/* +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 +*/ + #include "winpcapport.h" #include diff --git a/server/winpcapport.h b/server/winpcapport.h index dc0c84d..5ab2f9b 100644 --- a/server/winpcapport.h +++ b/server/winpcapport.h @@ -1,3 +1,22 @@ +/* +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 +*/ + #ifndef _SERVER_WIN_PCAP_PORT_H #define _SERVER_WIN_PCAP_PORT_H From 6d4be4272b2507379ae7447950a81a8f8f1e0da1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 27 Mar 2010 18:44:18 +0000 Subject: [PATCH 054/294] - Removed Copyright notice from client/modeltest.* files which are Trolltech provided and carry a Trolltech copyright notice - Added the GPLv3 license text - COPYING --- COPYING | 674 +++++++++++++++++++++++++++++++++++++++++++ client/modeltest.cpp | 19 -- client/modeltest.h | 19 -- 3 files changed, 674 insertions(+), 38 deletions(-) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/modeltest.cpp b/client/modeltest.cpp index 4b3de1f..2598c58 100644 --- a/client/modeltest.cpp +++ b/client/modeltest.cpp @@ -1,22 +1,3 @@ -/* -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 -*/ - /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. diff --git a/client/modeltest.h b/client/modeltest.h index 9b2d39f..38b6b2b 100644 --- a/client/modeltest.h +++ b/client/modeltest.h @@ -1,22 +1,3 @@ -/* -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 -*/ - /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. From 1cfc771daa5968769c48298b81b244c4e0fa8213 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 3 Apr 2010 08:18:29 +0000 Subject: [PATCH 055/294] - Added Preferences Dialog - Wireshark path can be configured by user in the preferences - Support for portable mode for preferences (.ini in same location as the executable) --- client/icons/preferences.png | Bin 0 -> 584 bytes client/main.cpp | 16 +++++ client/mainwindow.cpp | 10 +++ client/mainwindow.h | 1 + client/mainwindow.ui | 13 +++- client/ostinato.pro | 4 ++ client/ostinato.qrc | 1 + client/portgroup.cpp | 19 ++++-- client/preferences.cpp | 56 +++++++++++++++++ client/preferences.h | 41 +++++++++++++ client/preferences.ui | 114 +++++++++++++++++++++++++++++++++++ client/settings.h | 38 ++++++++++++ 12 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 client/icons/preferences.png create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h diff --git a/client/icons/preferences.png b/client/icons/preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..565a9330e0a156dff5bed2c9fad8c95a44344ba4 GIT binary patch literal 584 zcmV-O0=NB%P)g}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU0000 #include "mainwindow.h" #include +#include +#include + +QSettings *appSettings; QMainWindow *mainWindow; @@ -28,9 +32,21 @@ int main(int argc, char* argv[]) QApplication app(argc, argv); int exitCode; + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings("Ostinato", "Ostinato"); + mainWindow = new MainWindow; mainWindow->show(); exitCode = app.exec(); delete mainWindow; + delete appSettings; + return exitCode; } diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 2bd7655..b7d4145 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -26,6 +26,7 @@ along with this program. If not, see #include "portgrouplist.h" #include "portstatswindow.h" #include "portswindow.h" +#include "preferences.h" #include "ui_about.h" #include @@ -74,6 +75,15 @@ MainWindow::~MainWindow() delete localServer_; } +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + void MainWindow::on_actionHelpAbout_triggered() { QDialog *aboutDialog = new QDialog; diff --git a/client/mainwindow.h b/client/mainwindow.h index 0d8f02f..2f2602d 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -45,6 +45,7 @@ public: ~MainWindow(); public slots: + void on_actionPreferences_triggered(); void on_actionHelpAbout_triggered(); }; diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 1f93f5e..93b57e4 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -26,6 +26,7 @@ File + @@ -48,7 +49,17 @@ &About + + + :/icons/preferences.png + + + Preferences + + - + + + diff --git a/client/ostinato.pro b/client/ostinato.pro index 2738df9..853ca5c 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -37,6 +37,8 @@ HEADERS += \ portstatsfilterdialog.h \ portstatswindow.h \ portswindow.h \ + preferences.h \ + settings.h \ streamconfigdialog.h \ streamlistdelegate.h \ streammodel.h @@ -47,6 +49,7 @@ FORMS += \ portstatsfilter.ui \ portstatswindow.ui \ portswindow.ui \ + preferences.ui \ streamconfigdialog.ui SOURCES += \ @@ -64,6 +67,7 @@ SOURCES += \ portstatsfilterdialog.cpp \ portstatswindow.cpp \ portswindow.cpp \ + preferences.cpp \ streamconfigdialog.cpp \ streamlistdelegate.cpp \ streammodel.cpp diff --git a/client/ostinato.qrc b/client/ostinato.qrc index cb71e4c..5008ee9 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -24,6 +24,7 @@ icons/portstats_clear.png icons/portstats_clear_all.png icons/portstats_filter.png + icons/preferences.png icons/sound_mute.png icons/sound_none.png icons/stream_add.png diff --git a/client/portgroup.cpp b/client/portgroup.cpp index c5a8a2b..8e2597f 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -19,9 +19,12 @@ along with this program. If not, see #include "portgroup.h" +#include "settings.h" + #include #include #include +#include #include #include #include @@ -661,20 +664,26 @@ void PortGroup::processViewCaptureAck(PbRpcController *controller) { QFile *capFile = static_cast(controller->binaryBlob()); -#ifdef Q_OS_WIN32 - QString viewer("C:/Program Files/Wireshark/wireshark.exe"); -#else - QString viewer("/usr/bin/wireshark"); -#endif + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); qDebug("In %s", __FUNCTION__); capFile->flush(); capFile->close(); + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) qDebug("Failed starting Wireshark"); +_exit: delete controller; } diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..4f372ee --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,56 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..0bb2af4 --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, public Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..514ed42 --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,114 @@ + + Preferences + + + + 0 + 0 + 400 + 164 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + Wireshark Path + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..fa22e54 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,38 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#ifdef Q_OS_WIN32 +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +#endif + + From c630d4c29161ecb3be56bf570e675c34099d227b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 4 Apr 2010 08:15:39 +0000 Subject: [PATCH 056/294] - Added icons to some menu items - Fix: Changed Preferences to inherit privately from Ui::Preferences - Added "About Qt" to Help Menu - Added the PortList/StreamList actions to File Menu - Fix: "New Stream" action is enabled only when a port is selected in the port list - Fix: Ostinato now looks for Drone in the correct path --- client/icons/about.png | Bin 0 -> 1036 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/mainwindow.cpp | 17 ++++++++++++++--- client/mainwindow.ui | 16 ++++++++++++++++ client/ostinato.qrc | 3 +++ client/portswindow.cpp | 11 ++++++++++- client/portswindow.ui | 2 +- client/preferences.h | 2 +- 9 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 client/icons/about.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/qt.png diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV0000GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK700004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_setProcessChannelMode(QProcess::ForwardedChannels); - localServer_->start("./drone.exe"); + localServer_->start(serverApp); pgl = new PortGroupList; portsWindow = new PortsWindow(pgl, this); statsWindow = new PortStatsWindow(pgl, this); - portsDock = new QDockWidget(tr("Ports"), this); - statsDock = new QDockWidget(tr("Stats"), this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + statsDock = new QDockWidget(tr("Statistics"), this); setupUi(this); + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + statsDock->setWidget(statsWindow); addDockWidget(Qt::BottomDockWidgetArea, statsDock); portsDock->setWidget(portsWindow); addDockWidget(Qt::TopDockWidgetArea, portsDock); connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); #if 0 { DbgThread *dbg = new DbgThread(pgl); diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 93b57e4..28861d4 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -26,6 +26,7 @@ File + @@ -34,17 +35,24 @@ Help + + + :/icons/exit.png + E&xit + + :/icons/about.png + &About @@ -57,6 +65,14 @@ Preferences + + + :/icons/qt.png + + + About Qt + + diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 5008ee9..bd88fd1 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -1,5 +1,6 @@ + icons/about.png icons/arrow_down.png icons/arrow_left.png icons/arrow_right.png @@ -14,6 +15,7 @@ icons/control_stop.png icons/deco_exclusive.png icons/delete.png + icons/exit.png icons/logo.png icons/magnifier.png icons/name.png @@ -25,6 +27,7 @@ icons/portstats_clear_all.png icons/portstats_filter.png icons/preferences.png + icons/qt.png icons/sound_mute.png icons/sound_none.png icons/stream_add.png diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 0ef8a15..e0ad61e 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -55,6 +55,12 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDelete_Stream); + addActions(tvPortList->actions()); + QAction *sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + tvStreamList->setModel(plm->getStreamModel()); tvPortList->setModel(plm->getPortModel()); @@ -220,7 +226,10 @@ void PortsWindow::updateStreamViewActions() else { qDebug("No selection"); - actionNew_Stream->setEnabled(true); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } diff --git a/client/portswindow.ui b/client/portswindow.ui index 17361d4..3032b79 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -227,7 +227,7 @@ true - Exclusive Control (EXPERIMENTAL) + Exclusive Port Control (EXPERIMENTAL) diff --git a/client/preferences.h b/client/preferences.h index 0bb2af4..1821346 100644 --- a/client/preferences.h +++ b/client/preferences.h @@ -24,7 +24,7 @@ along with this program. If not, see #include -class Preferences : public QDialog, public Ui::Preferences +class Preferences : public QDialog, private Ui::Preferences { Q_OBJECT public: From 2af83212a6784c67a34f50c0e5345dec49aa0318 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 7 Apr 2010 21:13:54 +0530 Subject: [PATCH 057/294] Due to the switch to mercurial from svn, changed 'svnversion' to 'hg identify -i' --- version.pri | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.pri b/version.pri index df1335c..f19cb31 100644 --- a/version.pri +++ b/version.pri @@ -2,11 +2,11 @@ APP_VERSION = 0.1 APP_VERSION_FILE = version.cpp revtarget.target = $$APP_VERSION_FILE win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ - "const char *revision = \"$(shell svnversion .)\";" \ + "const char *revision = \"$(shell hg identify -i)\";" \ > $$APP_VERSION_FILE unix:revtarget.commands = echo \ "\"const char *version = \\\"$$APP_VERSION\\\";" \ - "const char *revision = \\\"$(shell svnversion .)\\\";\"" \ + "const char *revision = \\\"$(shell hg identify -i)\\\";\"" \ > $$APP_VERSION_FILE revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS From 836ed6e16de0869a6c88d88c54042b513d56c452 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 10 Apr 2010 17:04:54 +0530 Subject: [PATCH 058/294] Provided option to manually specify revision hash for source packages and tarballs --- version.pri | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/version.pri b/version.pri index f19cb31..d1f53fc 100644 --- a/version.pri +++ b/version.pri @@ -1,12 +1,15 @@ APP_VERSION = 0.1 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = APP_VERSION_FILE = version.cpp revtarget.target = $$APP_VERSION_FILE win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ - "const char *revision = \"$(shell hg identify -i)\";" \ + "const char *revision = \"$$APP_REVISION\";" \ > $$APP_VERSION_FILE unix:revtarget.commands = echo \ "\"const char *version = \\\"$$APP_VERSION\\\";" \ - "const char *revision = \\\"$(shell hg identify -i)\\\";\"" \ + "const char *revision = \\\"$APP_REVISION\\\";\"" \ > $$APP_VERSION_FILE revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS From 2e77fd8b947d3bcf5043a280c0d42f01628943b9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 10 Apr 2010 21:15:31 +0530 Subject: [PATCH 059/294] Adding a missing $ in version.pri for unix compilation --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index d1f53fc..103b6e9 100644 --- a/version.pri +++ b/version.pri @@ -9,7 +9,7 @@ win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ > $$APP_VERSION_FILE unix:revtarget.commands = echo \ "\"const char *version = \\\"$$APP_VERSION\\\";" \ - "const char *revision = \\\"$APP_REVISION\\\";\"" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ > $$APP_VERSION_FILE revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS From 35f4a8bafb8fdc57b5cb3266f1d04a5768cc1c0a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 16 Apr 2010 16:45:35 +0530 Subject: [PATCH 060/294] Fixes for 64bit compilation and Qt4.6 --- Makefile | 3 ++- client/main.cpp | 6 +++++- client/portstatsmodel.cpp | 16 ++++++++-------- client/streamconfigdialog.cpp | 12 ++++++------ common/icmp.cpp | 1 - common/protocollistiterator.cpp | 8 ++++---- common/protocolmanager.cpp | 2 +- common/streambase.cpp | 10 +++++----- server/drone_main.cpp | 5 +++++ 9 files changed, 36 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index 557a682..0b49aa8 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +debug: QMAKE_CONFIG=-config debug release: QMAKE_CONFIG=-config release all: @@ -7,7 +8,7 @@ all: $(MAKE) -C client release: qmake all - +debug: qmake all clean: -$(MAKE) -C client $@ diff --git a/client/main.cpp b/client/main.cpp index fd5daf8..91f61d8 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -18,13 +18,15 @@ along with this program. If not, see */ #include "mainwindow.h" +#include "../common/protocolmanager.h" #include #include #include -QSettings *appSettings; +extern ProtocolManager *OstProtocolManager; +QSettings *appSettings; QMainWindow *mainWindow; int main(int argc, char* argv[]) @@ -32,6 +34,8 @@ int main(int argc, char* argv[]) QApplication app(argc, argv); int exitCode; + OstProtocolManager = new ProtocolManager(); + /* (Portable Mode) If we have a .ini file in the same directory as the executable, we use that instead of the platform specific location and format for the settings */ diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 5bfd33e..92e6a24 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -130,28 +130,28 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const // Statistics case e_STAT_FRAMES_RCVD: - return stats.rx_pkts(); + return quint64(stats.rx_pkts()); case e_STAT_FRAMES_SENT: - return stats.tx_pkts(); + return quint64(stats.tx_pkts()); case e_STAT_FRAME_SEND_RATE: - return stats.tx_pps(); + return quint64(stats.tx_pps()); case e_STAT_FRAME_RECV_RATE: - return stats.rx_pps(); + return quint64(stats.rx_pps()); case e_STAT_BYTES_RCVD: - return stats.rx_bytes(); + return quint64(stats.rx_bytes()); case e_STAT_BYTES_SENT: - return stats.tx_bytes(); + return quint64(stats.tx_bytes()); case e_STAT_BYTE_SEND_RATE: - return stats.tx_bps(); + return quint64(stats.tx_bps()); case e_STAT_BYTE_RECV_RATE: - return stats.rx_bps(); + return quint64(stats.rx_bps()); #if 0 case e_STAT_FRAMES_RCVD_NIC: diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index dd02697..9609be3 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -28,7 +28,7 @@ along with this program. If not, see // FIXME(HI) - remove #include "../common/protocolmanager.h" -extern ProtocolManager OstProtocolManager; +extern ProtocolManager *OstProtocolManager; int StreamConfigDialog::lastTopLevelTabIndex = 0; @@ -95,7 +95,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, if (id2 != ButtonIdNone && id2 != ButtonIdOther) { - if (OstProtocolManager.isValidNeighbour(id1, id2)) + if (OstProtocolManager->isValidNeighbour(id1, id2)) { connect(btn1, SIGNAL(toggled(bool)), btn2, SLOT(setEnabled(bool))); @@ -117,7 +117,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, } mpAvailableProtocolsModel = new QStringListModel( - OstProtocolManager.protocolDatabase(), this); + OstProtocolManager->protocolDatabase(), this); lvAllProtocols->setModel(mpAvailableProtocolsModel); mpSelectedProtocolsModel = new QStringListModel(this); lvSelectedProtocols->setModel(mpSelectedProtocolsModel); @@ -375,7 +375,7 @@ void StreamConfigDialog::on_tbAdd_clicked() } foreach(QModelIndex idx, selection) - _iter->insert(OstProtocolManager.createProtocol( + _iter->insert(OstProtocolManager->createProtocol( mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); updateSelectProtocolsAdvancedWidget(); @@ -689,7 +689,7 @@ void StreamConfigDialog::__updateProtocol(int level, int newId) switch (oldId) { case ButtonIdNone: - _iter->insert(OstProtocolManager.createProtocol( + _iter->insert(OstProtocolManager->createProtocol( newId, mpStream)); break; @@ -699,7 +699,7 @@ void StreamConfigDialog::__updateProtocol(int level, int newId) p =_iter->next(); if (newId) - _iter->setValue(OstProtocolManager.createProtocol( + _iter->setValue(OstProtocolManager->createProtocol( newId, mpStream)); else _iter->remove(); diff --git a/common/icmp.cpp b/common/icmp.cpp index 0c03700..67290c4 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -215,7 +215,6 @@ QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, cksum = 0; // avoid the 'maybe used unitialized' warning break; } - printf("%s: attrib = %d, cksum = %d\n", __FUNCTION__, attrib, cksum); switch(attrib) { diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp index a081365..9f82c3d 100644 --- a/common/protocollistiterator.cpp +++ b/common/protocollistiterator.cpp @@ -33,12 +33,12 @@ ProtocolListIterator::~ProtocolListIterator() bool ProtocolListIterator::findNext(const AbstractProtocol* value) const { - return _iter->findNext((AbstractProtocol*)((uint)value)); + return _iter->findNext(const_cast(value)); } bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) { - return _iter->findPrevious((AbstractProtocol*)((uint)value)); + return _iter->findPrevious(const_cast(value)); } bool ProtocolListIterator::hasNext() const @@ -69,7 +69,7 @@ void ProtocolListIterator::insert(AbstractProtocol* value) else value->next = NULL; - _iter->insert((AbstractProtocol*)((uint)value)); + _iter->insert(const_cast(value)); } AbstractProtocol* ProtocolListIterator::next() @@ -109,7 +109,7 @@ void ProtocolListIterator::setValue(AbstractProtocol* value) const _iter->value()->next->prev = value; value->prev = _iter->value()->prev; value->next = _iter->value()->next; - _iter->setValue((AbstractProtocol*)((uint)value)); + _iter->setValue(const_cast(value)); } void ProtocolListIterator::toBack() diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 130e5ce..9e06468 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -39,7 +39,7 @@ along with this program. If not, see #include "userscript.h" #include "sample.h" -ProtocolManager OstProtocolManager; +ProtocolManager *OstProtocolManager; ProtocolManager::ProtocolManager() { diff --git a/common/streambase.cpp b/common/streambase.cpp index 109252c..44be9f1 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -23,7 +23,7 @@ along with this program. If not, see #include "protocollistiterator.h" #include "protocolmanager.h" -extern ProtocolManager OstProtocolManager; +extern ProtocolManager *OstProtocolManager; StreamBase::StreamBase() : mStreamId(new OstProto::StreamId), @@ -39,12 +39,12 @@ StreamBase::StreamBase() : iter = createProtocolListIterator(); // By default newly created streams have the mac and payload protocols - proto = OstProtocolManager.createProtocol( + proto = OstProtocolManager->createProtocol( OstProto::Protocol::kMacFieldNumber, this); iter->insert(proto); qDebug("stream: mac = %p", proto); - proto = OstProtocolManager.createProtocol( + proto = OstProtocolManager->createProtocol( OstProto::Protocol::kPayloadFieldNumber, this); iter->insert(proto); qDebug("stream: payload = %p", proto); @@ -89,7 +89,7 @@ void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) iter = createProtocolListIterator(); for (int i=0; i < stream.protocol_size(); i++) { - proto = OstProtocolManager.createProtocol( + proto = OstProtocolManager->createProtocol( stream.protocol(i).protocol_id().id(), this); proto->protoDataCopyFrom(stream.protocol(i)); iter->insert(proto); @@ -208,7 +208,7 @@ quint16 StreamBase::frameLen(int streamIndex) const case OstProto::StreamCore::e_fl_random: //! \todo (MED) This 'random' sequence is same across iterations pktLen = 64; // to avoid the 'maybe used uninitialized' warning - qsrand(((uint) this)); + qsrand(reinterpret_cast(this)); for (int i = 0; i <= streamIndex; i++) pktLen = qrand(); pktLen = frameLenMin() + (pktLen % diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 722f5c0..3f16bcc 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -19,12 +19,17 @@ along with this program. If not, see #include "drone.h" +#include "../common/protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + int myport; int main(int argc, char *argv[]) { QApplication app(argc, argv); Drone drone; + OstProtocolManager = new ProtocolManager(); app.setApplicationName(drone.objectName()); From 7b9c1f92a09112189cd9a32f82d76e70fc12102f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 16 Apr 2010 20:11:08 +0530 Subject: [PATCH 061/294] Fixed crash in PortStatsFilterDialog while removing selected rows --- client/portstatsfilterdialog.cpp | 56 +++++++++++++++----------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 457a7c8..baf9192 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -80,52 +80,48 @@ QList PortStatsFilterDialog::getItemList(bool* ok, void PortStatsFilterDialog::on_tbSelectIn_clicked() { - QStandardItem *item; - while (lvUnselected->selectionModel()->selectedIndexes().size()) + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + int count = mSelected.rowCount(); + + foreach(int row, rows) { - item = mUnselected.takeItem(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mUnselected.removeRow(lvUnselected->selectionModel()-> - selectedIndexes().at(0).row())) - mSelected.appendRow(item); + QList items = mUnselected.takeRow(row); + mSelected.insertRow(count, items); } } void PortStatsFilterDialog::on_tbSelectOut_clicked() { - QStandardItem *item; + QList rows; - while (lvSelected->selectionModel()->selectedIndexes().size()) + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) { - item = mSelected.takeItem(lvSelected->selectionModel()-> - selectedIndexes().at(0).row()); - if (mSelected.removeRow(lvSelected->selectionModel()-> - selectedIndexes().at(0).row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); } + + mUnselected.sort(0); } void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) { - QStandardItem *item; - - item = mUnselected.takeItem(index.row()); - if (mUnselected.removeRow(index.row())) - mSelected.appendRow(item); + QList items = mUnselected.takeRow(index.row()); + mSelected.appendRow(items); } void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) { - QStandardItem *item; - - item = mSelected.takeItem(index.row()); - if (mSelected.removeRow(index.row())) - { - mUnselected.appendRow(item); - mUnselected.sort(0); - } + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); } From 254e7141b04c1ab58360afb54f1f54baf0495db4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Apr 2010 17:32:46 +0530 Subject: [PATCH 062/294] PortStatsFilterDialog - ports can now be inserted at a specific row instead of just being added to the end --- client/portstatsfilterdialog.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index baf9192..55882f1 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -86,12 +86,13 @@ void PortStatsFilterDialog::on_tbSelectIn_clicked() rows.append(idx.row()); qSort(rows.begin(), rows.end(), qGreater()); - int count = mSelected.rowCount(); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); foreach(int row, rows) { QList items = mUnselected.takeRow(row); - mSelected.insertRow(count, items); + mSelected.insertRow(insertAt, items); } } @@ -115,7 +116,10 @@ void PortStatsFilterDialog::on_tbSelectOut_clicked() void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) { QList items = mUnselected.takeRow(index.row()); - mSelected.appendRow(items); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); } void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) From 3591227c0b0d53e6d060795b7e7a27ca20d6352b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Apr 2010 18:44:20 +0530 Subject: [PATCH 063/294] Fixed a assert failure when closing the client with the port list expanded --- client/portgroup.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 8e2597f..faa1f0a 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -78,7 +78,16 @@ PortGroup::~PortGroup() void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) { qDebug("state changed %d", state); - emit portGroupDataChanged(mPortGroupId); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } } void PortGroup::on_rpcChannel_connected() From 0075a3712b7b879ccaa51739328a08954de8f045 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 Apr 2010 19:57:28 +0530 Subject: [PATCH 064/294] Added .hgignore --- .hgignore | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .hgignore diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..b0a474f --- /dev/null +++ b/.hgignore @@ -0,0 +1,29 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +*\Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp + +# ctags +tags + From ae050372654a419fa7f0fd2ba9d10aa84b6881f1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 18 Apr 2010 21:50:35 +0530 Subject: [PATCH 065/294] Added, edited, corrected and completed the AbstractProtocol doxygen documentation --- common/abstractprotocol.cpp | 304 ++++++++++++++++++++++++++++++------ common/abstractprotocol.h | 50 +++--- common/sample.cpp | 2 + 3 files changed, 289 insertions(+), 67 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 750a26b..fdca539 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -26,20 +26,49 @@ along with this program. If not, see /*! \class AbstractProtocol - \todo (MED) update AbstractProtocol documentation - Bare Minimum set of methods that a subclass needs to reimplement + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() - protoDataCopyInto() [pure virtual] - protoDataCopyFrom() [pure virtual] - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] - Any useful protocol should also provide implementations for - - name() - - shortName() - - fieldName() + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() - Protocols with meta fields should additionally implement - - metaFieldCount() - - isMetaField() + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol */ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) { @@ -51,16 +80,31 @@ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) protoSize = -1; } +/*! + Destroys the abstract protocol +*/ AbstractProtocol::~AbstractProtocol() { } +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, AbstractProtocol* /* parent */) { return NULL; } +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ quint32 AbstractProtocol::protocolNumber() const { qFatal("Something wrong!!!"); @@ -68,29 +112,44 @@ quint32 AbstractProtocol::protocolNumber() const } /*! - \fn virtual void protoDataCopyInto(OstProto::OstProto::StreamCore &stream) = 0; + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 - Copy the protocol's protobuf into the passed in stream \n - In the base class this is a pure virtual function. Subclasses should - implement this function by using - \n - stream.AddExtension()->CopyFrom() */ + Copy the protocol's protobuf as an extension into the passed in protocol -/* - \fn virtual void protoDataCopyFrom(const OstProto::OstProto::StreamCore &stream) = 0; - */ + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ -/*! Returns the full name of the protocol \n - The default implementation returns a null string */ + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ QString AbstractProtocol::name() const { return QString(); } -/*! Returns the short name or abbreviation of the protocol \n - The default implementation forms and returns a abbreviation composed +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed of all the upper case chars in name() \n The default implementation caches the abbreviation on its first invocation - and subsequently returns the cached abbreviation */ + and subsequently returns the cached abbreviation +*/ QString AbstractProtocol::shortName() const { if (protoAbbr.isNull()) @@ -109,19 +168,27 @@ QString AbstractProtocol::shortName() const return protoAbbr; } -/*! Returns the number of fields (both Frame and Meta fields) \n - The default implementation returns zero */ -int AbstractProtocol::fieldCount() const +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const { return 0; } -/*! Returns the number of meta fields \n +/*! + Returns the number of meta fields + The default implementation counts and returns the number of fields for which the FieldIsMeta flag is set\n The default implementation caches the count on its first invocation - and subsequently returns the cached count */ -int AbstractProtocol::metaFieldCount() const + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const { if (metaCount < 0) { @@ -135,24 +202,35 @@ int AbstractProtocol::metaFieldCount() const return metaCount; } -/*! Returns the number of frame fields \n - Convenience method - same as fieldCount() minus metaFieldCount() */ -int AbstractProtocol::frameFieldCount() const +/*! + Returns the number of frame fields + + Convenience method - same as fieldCount() minus metaFieldCount() +*/ +int AbstractProtocol::frameFieldCount() const { //qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount()); return (fieldCount() - metaFieldCount()); } +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FieldIsNormal'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const { return FieldIsNormal; } -/*! Returns the requested field attribute data \n +/*! + Returns the requested field attribute data + Protocols which have meta fields that vary a frame field across streams may use the streamIndex to return the appropriate field value \n - Some field attriubutes e.g. FieldName may be invariant across streams\n - The default implementation returns the fieldValue() converted to string + Some field attributes e.g. FieldName may be invariant across streams\n The FieldTextValue attribute may include additional information about the field's value e.g. a checksum field may include "(correct)" or "(incorrect)" alongwith the actual checksum value. \n @@ -167,14 +245,14 @@ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const fields whose size are a non-integral multiple of bytes or smaller than a byte, subclasses should return the correct value. Also for fields which represent checksums, subclasses should return a value for - FieldBitSize - even if it is an integral multiple of bytes + FieldBitSize - even if it is an integral multiple of bytes. \note If a subclass uses any of the below functions to derive FieldFrameValue, the subclass should handle and return a value for FieldBitSize to prevent endless recrusion - - protocolFrameCksum() - protocolFramePayloadSize() - */ +*/ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -203,25 +281,58 @@ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, return QVariant(); } -/*! Sets the value of a field corresponding to index \n - Returns true if field is successfully set, false otherwise \n - The default implementation always returns false */ +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, FieldAttrib /*attrib*/) { return false; } +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const { return ProtocolIdNone; } +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const { return 0; } +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const { quint32 id; @@ -237,6 +348,16 @@ quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const return id; } +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ int AbstractProtocol::protocolFrameSize(int streamIndex) const { if (protoSize < 0) @@ -255,6 +376,13 @@ int AbstractProtocol::protocolFrameSize(int streamIndex) const return protoSize; } +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ int AbstractProtocol::protocolFrameOffset(int streamIndex) const { int size = 0; @@ -272,6 +400,12 @@ int AbstractProtocol::protocolFrameOffset(int streamIndex) const return size; } +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const { int size = 0; @@ -289,11 +423,14 @@ int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const } -/*! Returns a byte array encoding the protocol (and its fields) which can be +/*! + Returns a byte array encoding the protocol (and its fields) which can be inserted into the stream's frame + The default implementation forms and returns an ordered concatenation of - the FrameValue of all the 'frame' fields of the protocol taking care of fields - which are not an integral number of bytes\n */ + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const { QByteArray proto, field; @@ -386,16 +523,38 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) return proto; } +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ bool AbstractProtocol::isProtocolFrameValueVariable() const { return false; } +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ bool AbstractProtocol::isProtocolFrameSizeVariable() const { return false; } +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ bool AbstractProtocol::isProtocolFramePayloadValueVariable() const { AbstractProtocol *p = next; @@ -412,6 +571,13 @@ bool AbstractProtocol::isProtocolFramePayloadValueVariable() const return false; } +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const { AbstractProtocol *p = next; @@ -428,14 +594,17 @@ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const return false; } - /*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + \note If a subclass uses protocolFrameCksum() from within fieldData() to derive a cksum field, it MUST handle and return the 'FieldBitSize' attribute also for that particular field instead of using the default AbstractProtocol implementation for 'FieldBitSize' - this is required to prevent infinite recursion - */ +*/ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { @@ -502,6 +671,14 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, return cksum; } +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum + + Currently the default implementation supports only type CksumIpPseudo +*/ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, CksumType cksumType) const { @@ -530,6 +707,15 @@ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, return (quint16) ~sum; } +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum) + + Currently the default implementation supports only type CksumIp +*/ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, CksumType cksumType) const { @@ -558,3 +744,33 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, return (quint16) ~sum; } +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 9d12dd2..61ee58a 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -48,45 +48,49 @@ class AbstractProtocol friend class ProtocolListIterator; private: - mutable int metaCount; - mutable int protoSize; + mutable int metaCount; + mutable int protoSize; mutable QString protoAbbr; protected: - StreamBase *mpStream; - AbstractProtocol *parent; - AbstractProtocol *prev; - AbstractProtocol *next; + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol public: + //! Properties of a field, can be OR'd enum FieldFlag { - FieldIsNormal = 0x0, - FieldIsMeta = 0x1, - FieldIsCksum = 0x2 + FieldIsNormal = 0x0, //!< field appears in frame content + FieldIsMeta = 0x1, //!< field does not appear in frame, is meta data + FieldIsCksum = 0x2 //!< field is a checksum, appears in frame content }; - Q_DECLARE_FLAGS(FieldFlags, FieldFlag); + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + //! Various attributes of a field enum FieldAttrib { - FieldName, //! name - FieldValue, //! value in host byte order (user editable) - FieldTextValue, //! value as text - FieldFrameValue, //! frame encoded value in network byte order - FieldBitSize, //! size in bits + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits }; + //! Supported Protocol Id types enum ProtocolIdType { - ProtocolIdNone, - ProtocolIdLlc, - ProtocolIdEth, - ProtocolIdIp, + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP }; + //! Supported checksum types enum CksumType { - CksumIp, - CksumIpPseudo, - CksumTcpUdp, + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP - CksumMax + CksumMax //!< Marker for number of cksum types }; AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); diff --git a/common/sample.cpp b/common/sample.cpp index 4d601bf..d83ab54 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -30,6 +30,7 @@ SampleConfigForm::SampleConfigForm(QWidget *parent) SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { + /* The configWidget is created lazily */ configForm = NULL; } @@ -459,6 +460,7 @@ bool SampleProtocol::isProtocolFrameSizeVariable() const QWidget* SampleProtocol::configWidget() { + /* Lazy creation of the configWidget */ if (configForm == NULL) { configForm = new SampleConfigForm; From e681e1e22689acdb7e4054f961f658b1b74f008c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 24 Apr 2010 19:16:39 +0530 Subject: [PATCH 066/294] Added new param 'cksumScope' in AbstractProtocol's header and payload cksum methods; this ensures that TCP/UDP checksum is now correct if preceded by a IP 4over4. --- common/abstractprotocol.cpp | 32 ++++++++++++++++++++++++-------- common/abstractprotocol.h | 12 ++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index fdca539..8ed7513 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -672,15 +672,19 @@ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, } /*! - Returns the checksum of the requested type for the protocol's header + Returns the checksum of the requested type for the protocol's header This is useful for subclasses which needs the header's checksum e.g. TCP/UDP - require a "Pseudo-IP" checksum + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() */ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType, CksumScope cksumScope) const { quint32 sum = 0; quint16 cksum; @@ -692,15 +696,19 @@ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, { cksum = p->protocolFrameCksum(streamIndex, cksumType); sum += (quint16) ~cksum; - p = p->prev; qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; } if (parent) { - cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType); + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); sum += (quint16) ~cksum; } +out: while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); @@ -712,12 +720,16 @@ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP require a IP checksum of the payload (to be combined with other checksums to - derive the final checksum) + derive the final checksum). The checksum is limited to the specified + scope. Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() */ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, - CksumType cksumType) const + CksumType cksumType, CksumScope cksumScope) const { quint32 sum = 0; quint16 cksum; @@ -729,15 +741,19 @@ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, { cksum = p->protocolFrameCksum(streamIndex, cksumType); sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; p = p->next; } if (parent) { - cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType); + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); sum += (quint16) ~cksum; } +out: while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 61ee58a..f34beb9 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -93,6 +93,12 @@ public: CksumMax //!< Marker for number of cksum types }; + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~AbstractProtocol(); @@ -134,9 +140,11 @@ public: virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; virtual QWidget* configWidget() = 0; virtual void loadConfigWidget() = 0; From d0cddbafb8611befefcc2aa30a73b470a8dc0295 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 2 May 2010 15:37:46 +0530 Subject: [PATCH 067/294] Added a preflight check method to class StreamBase - this is used by StreamConfigDialog to notify the user of potential problems such as truncated frames --- client/streamconfigdialog.cpp | 13 +++++- client/streamconfigdialog.ui | 52 ++++++++-------------- common/dot3.cpp | 4 -- common/payload.cpp | 10 +++-- common/streambase.cpp | 81 +++++++++++++++++++++++++++++++++-- common/streambase.h | 8 +++- 6 files changed, 120 insertions(+), 48 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 9609be3..b58e2b3 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -926,11 +926,20 @@ void StreamConfigDialog::StoreCurrentStream() void StreamConfigDialog::on_pbOk_clicked() { - OstProto::Stream s; + QString log; + OstProto::Stream s; // Store dialog contents into stream StoreCurrentStream(); + if (!mpStream->preflightCheck(log)) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + // Copy the data from the "local working copy of stream" to "actual stream" mpStream->protoDataCopyInto(s); mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); @@ -938,5 +947,7 @@ void StreamConfigDialog::on_pbOk_clicked() qDebug("stream stored"); lastTopLevelTabIndex = twTopLevel->currentIndex(); + + accept(); } diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 07457e6..f4417f7 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -1198,22 +1198,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - pbOk - clicked() - StreamConfigDialog - accept() - - - 496 - 552 - - - 533 - 433 - - - pbCancel clicked() @@ -1221,8 +1205,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff reject() - 579 - 552 + 558 + 453 533 @@ -1237,12 +1221,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 158 - 149 + 169 + 207 - 158 - 149 + 169 + 207 @@ -1253,12 +1237,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 59 - 120 + 125 + 207 - 59 - 149 + 125 + 207 @@ -1269,12 +1253,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 59 - 123 + 125 + 207 - 59 - 149 + 125 + 207 @@ -1285,12 +1269,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 59 - 123 + 125 + 207 - 145 - 149 + 156 + 207 diff --git a/common/dot3.cpp b/common/dot3.cpp index adbe5d5..e97bdc0 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -23,7 +23,6 @@ along with this program. If not, see #include "dot3.h" #include "streambase.h" -#define SZ_FCS 4 Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) : QWidget(parent) @@ -95,7 +94,6 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, { quint16 len; - //len = mpStream->frameLen() - SZ_FCS; len = protocolFramePayloadSize(streamIndex); return len; } @@ -103,7 +101,6 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, { quint16 len; - //len = mpStream->frameLen() - SZ_FCS; len = protocolFramePayloadSize(streamIndex); return QString("%1").arg(len); } @@ -112,7 +109,6 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, quint16 len; QByteArray fv; - //len = mpStream->frameLen() - SZ_FCS; len = protocolFramePayloadSize(streamIndex); fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); diff --git a/common/payload.cpp b/common/payload.cpp index 2c6c267..4536a82 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -24,7 +24,6 @@ along with this program. If not, see #include "payload.h" #include "streambase.h" -#define SZ_FCS 4 PayloadConfigForm::PayloadConfigForm(QWidget *parent) : QWidget(parent) @@ -94,19 +93,22 @@ QString PayloadProtocol::shortName() const return QString("DATA"); } -int PayloadProtocol::protocolFrameSize(int streamIndex) const +int PayloadProtocol::protocolFrameSize(int streamIndex) const { int len; len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) - - SZ_FCS; + - kFcsSize; + + if (len < 0) + len = 0; qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, streamIndex, len); return len; } -int PayloadProtocol::fieldCount() const +int PayloadProtocol::fieldCount() const { return payload_fieldCount; } diff --git a/common/streambase.cpp b/common/streambase.cpp index 44be9f1..85909ba 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -362,14 +362,67 @@ _exit: return true; } -int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const { int pktLen, len = 0; - pktLen = frameLen(n); + pktLen = frameLen(frameIndex); // pktLen is adjusted for CRC/FCS which will be added by the NIC - pktLen -= 4; + pktLen -= kFcsSize; if ((pktLen < 0) || (pktLen > bufMaxSize)) return 0; @@ -383,7 +436,7 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const QByteArray ba; proto = iter->next(); - ba = proto->protocolFrameValue(n); + ba = proto->protocolFrameValue(frameIndex); if (len + ba.size() < bufMaxSize) memcpy(buf+len, ba.constData(), ba.size()); @@ -394,6 +447,26 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int n) const return pktLen; } +bool StreamBase::preflightCheck(QString &result) const +{ + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + goto _fail; + } + } + return true; + +_fail: + return false; +} + bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) { return stream1->ordinal() < stream2->ordinal() ? true : false; diff --git a/common/streambase.h b/common/streambase.h index 61964c6..7afbbd9 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -25,6 +25,8 @@ along with this program. If not, see #include "protocol.pb.h" +const int kFcsSize = 4; + class AbstractProtocol; class ProtocolList; class ProtocolListIterator; @@ -130,7 +132,11 @@ public: bool setBurstRate(quint32 burstsPerSec); bool isFrameVariable() const; - int frameValue(uchar *buf, int bufMaxSize, int n) const; + bool isFrameSizeVariable() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); }; From d9f788e93d3a1c3b103ac6ff55f2669fe6ca3cd8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 4 May 2010 21:42:29 +0530 Subject: [PATCH 068/294] Fixed compiler warnings --- client/ostinato.pro | 2 +- client/portgrouplist.cpp | 4 +++- client/portswindow.cpp | 8 ++++---- client/streamconfigdialog.cpp | 11 ++++++++--- common/streambase.cpp | 2 +- rpc/pbrpcchannel.cpp | 15 ++++++++------- rpc/rpcserver.cpp | 23 ++++++++++++----------- server/abstractport.cpp | 7 ++++--- server/pcapport.cpp | 4 ++-- server/pcapport.h | 2 +- 10 files changed, 44 insertions(+), 34 deletions(-) diff --git a/client/ostinato.pro b/client/ostinato.pro index 853ca5c..3655802 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -78,4 +78,4 @@ QMAKE_DISTCLEAN += object_script.* include(../version.pri) # TODO(LOW): Test only -include(modeltest.pri) +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index 3d6a8be..a8e688c 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -29,11 +29,13 @@ PortGroupList::PortGroupList() { PortGroup *pg; +#ifndef QT_NO_DEBUG // TODO(LOW): Remove streamModelTester_ = new ModelTest(getStreamModel()); portModelTester_ = new ModelTest(getPortModel()); portStatsModelTester_ = new ModelTest(getPortStatsModel()); - +#endif + // Add the "Local" Port Group pg = new PortGroup; addPortGroup(*pg); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index e0ad61e..ccaed5a 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -168,10 +168,10 @@ void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, if ((tvPortList->currentIndex() >= topLeft) && (tvPortList->currentIndex() <= bottomRight)) #endif - if ((topLeft < tvPortList->currentIndex()) || - (topLeft == tvPortList->currentIndex()) && - ((tvPortList->currentIndex() < bottomRight)) || - (tvPortList->currentIndex() == bottomRight)) + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) { updatePortViewActions(tvPortList->currentIndex()); } diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index b58e2b3..32ebe01 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -135,7 +135,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, LoadCurrentStream(); mpPacketModel = new PacketModel(this); tvPacketTree->setModel(mpPacketModel); +#ifndef QT_NO_DEBUG mpPacketModelTester = new ModelTest(mpPacketModel); +#endif tvPacketTree->header()->hide(); vwPacketDump->setModel(mpPacketModel); vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); @@ -386,7 +388,7 @@ void StreamConfigDialog::on_tbDelete_clicked() { int n; QModelIndex idx; - AbstractProtocol *p; + AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); @@ -405,6 +407,7 @@ void StreamConfigDialog::on_tbDelete_clicked() p = _iter->next(); } + Q_CHECK_PTR(p); _iter->remove(); delete p; @@ -416,7 +419,7 @@ void StreamConfigDialog::on_tbUp_clicked() { int m, n; QModelIndex idx; - AbstractProtocol *p; + AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); @@ -435,6 +438,7 @@ void StreamConfigDialog::on_tbUp_clicked() p = _iter->next(); } + Q_CHECK_PTR(p); _iter->remove(); _iter->previous(); _iter->insert(p); @@ -447,7 +451,7 @@ void StreamConfigDialog::on_tbDown_clicked() { int m, n; QModelIndex idx; - AbstractProtocol *p; + AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); @@ -466,6 +470,7 @@ void StreamConfigDialog::on_tbDown_clicked() p = _iter->next(); } + Q_CHECK_PTR(p); _iter->remove(); _iter->next(); _iter->insert(p); diff --git a/common/streambase.cpp b/common/streambase.cpp index 85909ba..6ba1909 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -403,7 +403,7 @@ int StreamBase::frameProtocolLength(int frameIndex) const int StreamBase::frameCount() const { - int count; + int count = 0; switch (sendUnit()) { diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 521aca5..c89a2d6 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -82,7 +82,8 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char msg[MSGBUF_SIZE]; + char msgBuf[MSGBUF_SIZE]; + char* const msg = &msgBuf[0]; int len; bool ret; @@ -110,7 +111,7 @@ void PbRpcChannel::CallMethod( if (!req->IsInitialized()) { qWarning("RpcChannel: missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); + qDebug("%s", req->InitializationErrorString().c_str()); qFatal("exiting"); @@ -125,13 +126,13 @@ void PbRpcChannel::CallMethod( this->response=response; isPending = true; - ret = req->SerializeToArray((void*) (&msg[PB_HDR_SIZE]), sizeof(msg)); + ret = req->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); Q_ASSERT(ret == true); len = req->ByteSize(); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_REQUEST); // type - *((quint16*)(&msg[2])) = HTONS(method->index()); // method id - *((quint32*)(&msg[4])) = HTONL(len); // len + *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_REQUEST); // type + *((quint16*)(msg+2)) = HTONS(method->index()); // method id + *((quint32*)(msg+4)) = HTONL(len); // len // Avoid printing stats since it happens every couple of seconds if (pendingMethodId != 13) @@ -260,7 +261,7 @@ void PbRpcChannel::on_mpSocket_readyRead() if (!response->IsInitialized()) { qWarning("RpcChannel: missing required fields in response"); - qDebug(response->InitializationErrorString().c_str()); + qDebug("%s", response->InitializationErrorString().c_str()); controller->SetFailed("Required fields missing"); } diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 5d67833..4b86b25 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -69,7 +69,8 @@ void RpcServer::done(PbRpcController *controller) { google::protobuf::Message *response = controller->response(); QIODevice *blob; - char msg[MSGBUF_SIZE]; + char msgBuf[MSGBUF_SIZE]; + char* const msg = &msgBuf[0]; int len; //qDebug("In RpcServer::done"); @@ -86,9 +87,9 @@ void RpcServer::done(PbRpcController *controller) len = blob->size(); qDebug("is binary blob of len %d", len); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_BINBLOB); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - (*(quint32*)(&msg[4])) = HTONL(len); // len + *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_BINBLOB); // type + *((quint16*)(msg+2)) = HTONS(pendingMethodId); // method + (*(quint32*)(msg+4)) = HTONL(len); // len clientSock->write(msg, PB_HDR_SIZE); @@ -97,7 +98,7 @@ void RpcServer::done(PbRpcController *controller) { int l; - len = blob->read(msg, sizeof(msg)); + len = blob->read(msg, sizeof(msgBuf)); l = clientSock->write(msg, len); Q_ASSERT(l == len); } @@ -108,18 +109,18 @@ void RpcServer::done(PbRpcController *controller) if (!response->IsInitialized()) { qWarning("response missing required fields!!"); - qDebug(response->InitializationErrorString().c_str()); + qDebug("%s", response->InitializationErrorString().c_str()); qFatal("exiting"); goto _exit; } - response->SerializeToArray((void*) &msg[PB_HDR_SIZE], sizeof(msg)); + response->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); len = response->ByteSize(); - *((quint16*)(&msg[0])) = HTONS(PB_MSG_TYPE_RESPONSE); // type - *((quint16*)(&msg[2])) = HTONS(pendingMethodId); // method - *((quint32*)(&msg[4])) = HTONL(len); // len + *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_RESPONSE); // type + *((quint16*)(msg+2)) = HTONS(pendingMethodId); // method + *((quint32*)(msg+4)) = HTONL(len); // len // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 13) @@ -248,7 +249,7 @@ void RpcServer::when_dataAvail() if (!req->IsInitialized()) { qWarning("Missing required fields in request"); - qDebug(req->InitializationErrorString().c_str()); + qDebug("%s", req->InitializationErrorString().c_str()); qFatal("exiting"); delete req; delete resp; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 70d833f..954278b 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -76,7 +76,7 @@ StreamBase* AbstractPort::stream(int streamId) { for (int i = 0; i < streamList_.size(); i++) { - if (streamId == streamList_.at(i)->id()) + if ((uint)streamId == streamList_.at(i)->id()) return streamList_.at(i); } @@ -96,7 +96,7 @@ bool AbstractPort::deleteStream(int streamId) { StreamBase *stream; - if (streamId == streamList_.at(i)->id()) + if ((uint)streamId == streamList_.at(i)->id()) { stream = streamList_.takeAt(i); delete stream; @@ -157,6 +157,7 @@ void AbstractPort::updatePacketList() if (streamList_[i]->isFrameVariable()) { isVariable = true; + len = 0; // avoid compiler warning; get len value for each pkt } else { @@ -216,7 +217,7 @@ void AbstractPort::updatePacketList() */ setPacketListLoopMode(true, streamList_[i]->sendUnit() == - OstProto::StreamControl::e_su_bursts ? ibg : ipg); + StreamBase::e_su_bursts ? ibg : ipg); goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_next: diff --git a/server/pcapport.cpp b/server/pcapport.cpp index f9412cf..222dd42 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -132,6 +132,7 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, ret = pcap_setdirection(handle_, PCAP_D_OUT); break; default: + ret = -1; // avoid 'may be used uninitialized' warning Q_ASSERT(false); } #endif @@ -351,8 +352,7 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; char *end = queue->buffer + queue->len; - if (sync) - ts = hdr->ts; + ts = hdr->ts; while (1) { diff --git a/server/pcapport.h b/server/pcapport.h index 0001b0a..a6264c5 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -36,7 +36,7 @@ public: void init(); virtual bool hasExclusiveControl() { return false; } - virtual bool setExclusiveControl(bool exclusive) { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } virtual void clearPacketList() { transmitter_->clearPacketList(); From 3defe905e5b227a7a181cc4833482ffce7cbdaaf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 6 May 2010 20:39:29 +0530 Subject: [PATCH 069/294] Added a generic TextProtocol that can be used for any text based protocol such as HTTP, SIP, RTSP, NNTP etc. Also - fixed a bug in ARP; IntComboBox sets itself as 'editable' --- common/arp.cpp | 8 ++ common/intcombobox.h | 1 + common/ostproto.pro | 4 + common/protocol.proto | 2 + common/protocolmanager.cpp | 9 +- common/textproto.cpp | 259 +++++++++++++++++++++++++++++++++++++ common/textproto.h | 89 +++++++++++++ common/textproto.proto | 37 ++++++ common/textproto.ui | 79 +++++++++++ 9 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui diff --git a/common/arp.cpp b/common/arp.cpp index 7548c62..84a6094 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -722,6 +722,8 @@ bool ArpProtocol::setFieldData(int index, const QVariant &value, uint mode = value.toUInt(&isOk); if (isOk && data.HwAddrMode_IsValid(mode)) data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; break; } case arp_senderHwAddrCount: @@ -745,6 +747,8 @@ bool ArpProtocol::setFieldData(int index, const QVariant &value, if (isOk && data.ProtoAddrMode_IsValid(mode)) data.set_sender_proto_addr_mode( (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; break; } case arp_senderProtoAddrCount: @@ -774,6 +778,8 @@ bool ArpProtocol::setFieldData(int index, const QVariant &value, uint mode = value.toUInt(&isOk); if (isOk && data.HwAddrMode_IsValid(mode)) data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; break; } case arp_targetHwAddrCount: @@ -797,6 +803,8 @@ bool ArpProtocol::setFieldData(int index, const QVariant &value, if (isOk && data.ProtoAddrMode_IsValid(mode)) data.set_target_proto_addr_mode( (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; break; } case arp_targetProtoAddrCount: diff --git a/common/intcombobox.h b/common/intcombobox.h index 7da3351..2884a3a 100644 --- a/common/intcombobox.h +++ b/common/intcombobox.h @@ -28,6 +28,7 @@ public: IntComboBox(QWidget *parent = 0) : QComboBox(parent) { + setEditable(true); } void addItem(int value, const QString &text) { diff --git a/common/ostproto.pro b/common/ostproto.pro index 2c8aaf3..33567f8 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -16,6 +16,7 @@ FORMS += \ icmp.ui \ tcp.ui \ udp.ui \ + textproto.ui \ userscript.ui \ sample.ui PROTOS += \ @@ -36,6 +37,7 @@ PROTOS += \ icmp.proto \ tcp.proto \ udp.proto \ + textproto.proto \ userscript.proto \ sample.proto HEADERS += \ @@ -61,6 +63,7 @@ HEADERS += \ icmp.h \ tcp.h \ udp.h \ + textproto.h \ userscript.h \ sample.h SOURCES += \ @@ -82,6 +85,7 @@ SOURCES += \ icmp.cpp \ tcp.cpp \ udp.cpp \ + textproto.cpp \ userscript.cpp \ sample.cpp diff --git a/common/protocol.proto b/common/protocol.proto index 4bd5899..bafbe8a 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -106,6 +106,8 @@ message Protocol { kUdpFieldNumber = 141; kIcmpFieldNumber = 142; kIgmpFieldNumber = 143; + + kTextProtocolFieldNumber = 150; } } diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 9e06468..b543ac0 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -36,6 +36,7 @@ along with this program. If not, see #include "icmp.h" #include "tcp.h" #include "udp.h" +#include "textproto.h" #include "userscript.h" #include "sample.h" @@ -48,8 +49,6 @@ ProtocolManager::ProtocolManager() */ registerProtocol(OstProto::Protocol::kMacFieldNumber, (void*) MacProtocol::createInstance); - registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kDot3FieldNumber, @@ -79,6 +78,12 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..2f69f82 --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,259 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + //case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_encoding: + flags |= FieldIsMeta; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + return QString().fromStdString(data.text()).toUtf8(); + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue).toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..57b71cc --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text encoded with the specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..be4f246 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + optional uint32 port_num = 1 [default = 0]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; +} + +extend Protocol { + optional TextProtocol textProtocol = 150; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..b3cf278 --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,79 @@ + + TextProtocol + + + + 0 + 0 + 493 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + +
From 6ca88eb661b5adbb72914016f86d4ba9a7baaf3d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 8 May 2010 20:12:38 +0530 Subject: [PATCH 070/294] StreamConfigDialog "Protocol Selection" framework has been enhanced to include a L5 group. TextProtocol has been added to this L5 group. TCP/UDP protocols have been modified to get port numbers from a L5 protocol; TCP/UDP protocols have also been updated as per the "Sample" protocol recommendation --- client/streamconfigdialog.cpp | 37 ++++- client/streamconfigdialog.h | 7 +- client/streamconfigdialog.ui | 142 ++++++++++------ common/abstractprotocol.h | 1 + common/tcp.cpp | 298 +++++++++++++++++++++++++++++----- common/tcp.h | 6 +- common/tcp.proto | 25 +-- common/tcp.ui | 52 +++++- common/textproto.cpp | 5 +- common/textproto.proto | 2 +- common/udp.cpp | 217 +++++++++++++++++++++---- common/udp.h | 6 +- common/udp.proto | 14 +- common/udp.ui | 62 +++++-- 14 files changed, 709 insertions(+), 165 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 32ebe01..7789f8d 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -66,6 +66,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and // disable the subsequent protocol group as well @@ -78,8 +79,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); - // Setup valid subsequent protocols for L2 and L3 protocols - for (int i = ProtoL2; i <= ProtoL3; i++) + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) { foreach(QAbstractButton *btn1, bgProto[i]->buttons()) { @@ -207,7 +208,7 @@ void StreamConfigDialog::setupUiExtra() foreach(QRadioButton *btn, gbL4Proto->findChildren()) bgProto[ProtoL4]->addButton(btn); #else - bgProto[ProtoL4]->addButton(rbL4None, 0); + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); @@ -215,6 +216,17 @@ void StreamConfigDialog::setupUiExtra() bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); #endif + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + bgProto[ProtoPayload] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbPayloadProto->findChildren()) @@ -639,13 +651,19 @@ void StreamConfigDialog::forceProtocolNone(bool checked) bgProto[ProtoL3]->button(ButtonIdNone)->click(); disableProtocols(bgProto[ProtoL3], checked); } - else if (btn == rbL3None) + else if (btn == rbL3None) { if (checked) bgProto[ProtoL4]->button(ButtonIdNone)->click(); disableProtocols(bgProto[ProtoL4], checked); } - else + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else { Q_ASSERT(1 == 0); // Unreachable code! } @@ -929,6 +947,15 @@ void StreamConfigDialog::StoreCurrentStream() } } +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +} + void StreamConfigDialog::on_pbOk_clicked() { QString log; diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 637bc9c..5709570 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -61,7 +61,8 @@ private: ProtoL2 = 2, ProtoL3 = 3, ProtoL4 = 4, - ProtoPayload = 5, + ProtoL5 = 5, + ProtoPayload = 6, ProtoMax }; @@ -87,7 +88,6 @@ private: static int lastTopLevelTabIndex; void setupUiExtra(); - void updateSelectedProtocols(); void LoadCurrentStream(); void StoreCurrentStream(); @@ -122,6 +122,9 @@ private slots: void updateSelectProtocolsAdvancedWidget(); + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + void on_pbPrev_clicked(); void on_pbNext_clicked(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index f4417f7..f64c848 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -9,7 +9,7 @@ 0 0 569 - 464 + 481 @@ -174,14 +174,14 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 527 - 226 + 243 Simple - + L1 @@ -220,7 +220,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
- + true @@ -351,7 +351,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + true @@ -393,49 +393,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - true - - - VLAN - - - false - - - false - - - - - - Untagged - - - true - - - - - - - Tagged - - - - - - - Stacked - - - - - - - + true @@ -507,7 +465,49 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + Qt::Vertical @@ -515,11 +515,53 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 20 - 21 + 71 + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index f34beb9..a36bbef 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -82,6 +82,7 @@ public: ProtocolIdLlc, //!< LLC (802.2) ProtocolIdEth, //!< Ethernet II ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number }; //! Supported checksum types diff --git a/common/tcp.cpp b/common/tcp.cpp index 9c44ec0..f29b1a1 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -73,6 +73,11 @@ QString TcpProtocol::shortName() const return QString("TCP"); } +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + quint32 TcpProtocol::protocolId(ProtocolIdType type) const { switch(type) @@ -84,7 +89,7 @@ quint32 TcpProtocol::protocolId(ProtocolIdType type) const return AbstractProtocol::protocolId(type); } -int TcpProtocol::fieldCount() const +int TcpProtocol::fieldCount() const { return tcp_fieldCount; } @@ -114,12 +119,16 @@ AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const case tcp_urg_ptr: break; + case tcp_is_override_src_port: + case tcp_is_override_dst_port: case tcp_is_override_hdrlen: case tcp_is_override_cksum: flags |= FieldIsMeta; break; default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } @@ -132,47 +141,81 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, switch (index) { case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } switch(attrib) { case FieldName: return QString("Source Port"); case FieldValue: - return data.src_port(); + return srcPort; case FieldTextValue: - return QString("%1").arg(data.src_port()); + return QString("%1").arg(srcPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); - qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + qToBigEndian(srcPort, (uchar*) fv.data()); return fv; } default: break; } break; - + } case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } switch(attrib) { case FieldName: return QString("Destination Port"); case FieldValue: - return data.dst_port(); + return dstPort; case FieldTextValue: - return QString("%1").arg(data.dst_port()); + return QString("%1").arg(dstPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); - qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + qToBigEndian(dstPort, (uchar*) fv.data()); return fv; } default: break; } break; - + } case tcp_seq_num: switch(attrib) { @@ -384,19 +427,174 @@ QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, break; // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool TcpProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; } bool TcpProtocol::isProtocolFrameValueVariable() const @@ -421,53 +619,73 @@ void TcpProtocol::loadConfigWidget() { configWidget(); - configForm->leTcpSrcPort->setText(QString().setNum(data.src_port())); - configForm->leTcpDstPort->setText(QString().setNum(data.dst_port())); + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); - configForm->leTcpSeqNum->setText(QString().setNum(data.seq_num())); - configForm->leTcpAckNum->setText(QString().setNum(data.ack_num())); + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); - configForm->leTcpHdrLen->setText(fieldData(tcp_hdrlen, FieldValue).toString()); - configForm->cbTcpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); - configForm->leTcpWindow->setText(QString().setNum(data.window())); + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); configForm->leTcpCksum->setText(QString("%1").arg( fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbTcpCksumOverride->setChecked(data.is_override_cksum()); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); - configForm->leTcpUrgentPointer->setText(QString().setNum(data.urg_ptr())); - - configForm->cbTcpFlagsUrg->setChecked((data.flags() & TCP_FLAG_URG) > 0); - configForm->cbTcpFlagsAck->setChecked((data.flags() & TCP_FLAG_ACK) > 0); - configForm->cbTcpFlagsPsh->setChecked((data.flags() & TCP_FLAG_PSH) > 0); - configForm->cbTcpFlagsRst->setChecked((data.flags() & TCP_FLAG_RST) > 0); - configForm->cbTcpFlagsSyn->setChecked((data.flags() & TCP_FLAG_SYN) > 0); - configForm->cbTcpFlagsFin->setChecked((data.flags() & TCP_FLAG_FIN) > 0); + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); } void TcpProtocol::storeConfigWidget() { - bool isOk; int ff = 0; configWidget(); - data.set_src_port(configForm->leTcpSrcPort->text().toULong(&isOk)); - data.set_dst_port(configForm->leTcpDstPort->text().toULong(&isOk)); + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); - data.set_seq_num(configForm->leTcpSeqNum->text().toULong(&isOk)); - data.set_ack_num(configForm->leTcpAckNum->text().toULong(&isOk)); + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); - data.set_hdrlen_rsvd((configForm->leTcpHdrLen->text().toULong(&isOk) << 4) & 0xF0); - data.set_is_override_hdrlen(configForm->cbTcpHdrLenOverride->isChecked()); + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); - data.set_window(configForm->leTcpWindow->text().toULong(&isOk)); + setFieldData(tcp_window, configForm->leTcpWindow->text()); - data.set_cksum(configForm->leTcpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_is_override_cksum(configForm->cbTcpCksumOverride->isChecked()); + setFieldData(tcp_cksum, configForm->leTcpCksum->text()); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); - data.set_urg_ptr(configForm->leTcpUrgentPointer->text().toULong(&isOk)); + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; @@ -475,6 +693,6 @@ void TcpProtocol::storeConfigWidget() if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; - data.set_flags(ff); + setFieldData(tcp_flags, ff); } diff --git a/common/tcp.h b/common/tcp.h index 3b32b35..265937a 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -57,6 +57,8 @@ private: tcp_cksum, tcp_urg_ptr, + tcp_is_override_src_port, + tcp_is_override_dst_port, tcp_is_override_hdrlen, tcp_is_override_cksum, @@ -76,9 +78,11 @@ public: virtual QString name() const; virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/tcp.proto b/common/tcp.proto index 39d96be..3aa4382 100644 --- a/common/tcp.proto +++ b/common/tcp.proto @@ -22,22 +22,23 @@ import "protocol.proto"; package OstProto; // Tcp message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; - optional bool is_override_hdrlen = 1; - optional bool is_override_cksum = 2; + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; - optional uint32 seq_num = 5 [default = 129018]; - optional uint32 ack_num = 6; + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; - optional uint32 hdrlen_rsvd = 7 [default = 0x50]; - optional uint32 flags = 8; - - optional uint32 window = 9 [default = 1024]; - optional uint32 cksum = 10; - optional uint32 urg_ptr = 11; + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; } extend Protocol { diff --git a/common/tcp.ui b/common/tcp.ui index e19e9fb..6f3eee8 100644 --- a/common/tcp.ui +++ b/common/tcp.ui @@ -14,14 +14,18 @@ - + - Source Port + Override Source Port - + + + false + + @@ -48,14 +52,18 @@ - + - Destination Port + Override Destination Port - + + + false + + @@ -224,5 +232,37 @@ + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + diff --git a/common/textproto.cpp b/common/textproto.cpp index 2f69f82..90327ea 100644 --- a/common/textproto.cpp +++ b/common/textproto.cpp @@ -83,7 +83,7 @@ quint32 TextProtocol::protocolId(ProtocolIdType type) const { switch(type) { - //case ProtocolIdTcpUdp: return data.port_num(); + case ProtocolIdTcpUdp: return data.port_num(); default:break; } @@ -220,7 +220,8 @@ _exit: int TextProtocol::protocolFrameSize(int streamIndex) const { - return fieldData(textProto_text, FieldFrameValue).toByteArray().size() ; + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; } QWidget* TextProtocol::configWidget() diff --git a/common/textproto.proto b/common/textproto.proto index be4f246..8bb290b 100644 --- a/common/textproto.proto +++ b/common/textproto.proto @@ -27,7 +27,7 @@ message TextProtocol { kUtf8 = 0; } - optional uint32 port_num = 1 [default = 0]; + optional uint32 port_num = 1 [default = 80]; optional TextEncoding encoding = 2 [default = kUtf8]; optional string text = 3; } diff --git a/common/udp.cpp b/common/udp.cpp index 7bb74e3..4097ff8 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -73,6 +73,11 @@ QString UdpProtocol::shortName() const return QString("UDP"); } +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + quint32 UdpProtocol::protocolId(ProtocolIdType type) const { switch(type) @@ -84,7 +89,7 @@ quint32 UdpProtocol::protocolId(ProtocolIdType type) const return AbstractProtocol::protocolId(type); } -int UdpProtocol::fieldCount() const +int UdpProtocol::fieldCount() const { return udp_fieldCount; } @@ -106,12 +111,16 @@ AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const flags |= FieldIsCksum; break; + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: case udp_isOverrideTotLen: case udp_isOverrideCksum: flags |= FieldIsMeta; break; default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } @@ -124,47 +133,81 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, switch (index) { case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } switch(attrib) { case FieldName: return QString("Source Port"); case FieldValue: - return data.src_port(); + return srcPort; case FieldTextValue: - return QString("%1").arg(data.src_port()); + return QString("%1").arg(srcPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); - qToBigEndian((quint16) data.src_port(), (uchar*) fv.data()); + qToBigEndian(srcPort, (uchar*) fv.data()); return fv; } default: break; } break; - + } case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } switch(attrib) { case FieldName: return QString("Destination Port"); case FieldValue: - return data.dst_port(); + return dstPort; case FieldTextValue: - return QString("%1").arg(data.dst_port()); + return QString("%1").arg(dstPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); - qToBigEndian((quint16) data.dst_port(), (uchar*) fv.data()); + qToBigEndian(dstPort, (uchar*) fv.data()); return fv; } default: break; } break; - + } case udp_totLen: { @@ -253,30 +296,132 @@ QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, } break; } + // Meta fields - case udp_isOverrideTotLen: - case udp_isOverrideCksum: + case udp_isOverrideSrcPort: + { switch(attrib) { - case FieldIsMeta: - return true; + case FieldValue: + return data.is_override_src_port(); default: break; } break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool UdpProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) { - //! implement UdpProtocol::setFieldData() - return false; + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; } bool UdpProtocol::isProtocolFrameValueVariable() const @@ -301,15 +446,24 @@ void UdpProtocol::loadConfigWidget() { configWidget(); - configForm->leUdpSrcPort->setText(fieldData(udp_srcPort, FieldValue).toString()); - configForm->leUdpDstPort->setText(fieldData(udp_dstPort, FieldValue).toString()); + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); - configForm->leUdpLength->setText(fieldData(udp_totLen, FieldValue).toString()); - configForm->cbUdpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); configForm->leUdpCksum->setText(QString("%1").arg( fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbUdpCksumOverride->setChecked(data.is_override_cksum()); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); } void UdpProtocol::storeConfigWidget() @@ -318,13 +472,20 @@ void UdpProtocol::storeConfigWidget() configWidget(); - data.set_src_port(configForm->leUdpSrcPort->text().toULong(&isOk)); - data.set_dst_port(configForm->leUdpDstPort->text().toULong(&isOk)); + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); - data.set_totlen(configForm->leUdpLength->text().toULong(&isOk)); - data.set_is_override_totlen(configForm->cbUdpLengthOverride->isChecked()); + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); - data.set_cksum(configForm->leUdpCksum->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_is_override_cksum(configForm->cbUdpCksumOverride->isChecked()); + setFieldData(udp_cksum, configForm->leUdpCksum->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); } diff --git a/common/udp.h b/common/udp.h index 86c2ca8..623e999 100644 --- a/common/udp.h +++ b/common/udp.h @@ -44,6 +44,8 @@ private: udp_totLen, udp_cksum, + udp_isOverrideSrcPort, + udp_isOverrideDstPort, udp_isOverrideTotLen, udp_isOverrideCksum, @@ -63,9 +65,11 @@ public: virtual QString name() const; virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/udp.proto b/common/udp.proto index 5c30eac..8852c11 100644 --- a/common/udp.proto +++ b/common/udp.proto @@ -23,13 +23,15 @@ package OstProto; // UDP message Udp { - optional bool is_override_totlen = 1; - optional bool is_override_cksum = 2; + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; - optional uint32 src_port = 3 [default = 8902]; - optional uint32 dst_port = 4 [default = 80]; - optional uint32 totlen = 5; - optional uint32 cksum = 6; + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; } extend Protocol { diff --git a/common/udp.ui b/common/udp.ui index c5c9862..ab979e9 100644 --- a/common/udp.ui +++ b/common/udp.ui @@ -5,7 +5,7 @@ 0 0 - 217 + 246 144 @@ -16,24 +16,32 @@ - + - Source Port + Override Source Port - + + + false + + - + - Destination Port + Override Destination Port - + + + false + + @@ -109,8 +117,8 @@ 63 - 149 - 63 + 209 + 81 @@ -125,8 +133,40 @@ 106 - 158 - 106 + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 From 564b30955bd317aaa6ec633eab691d1fa363ce35 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 8 May 2010 20:44:33 +0530 Subject: [PATCH 071/294] Removed hardcoded paths from pbrpc.pro; made MainWindow default size smaller --- client/mainwindow.ui | 6 +++--- rpc/pbrpc.pro | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 28861d4..45db008 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -5,8 +5,8 @@ 0 0 - 800 - 650 + 700 + 550 @@ -18,7 +18,7 @@ 0 0 - 800 + 700 21 diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro index bcd5b8d..27ec61b 100644 --- a/rpc/pbrpc.pro +++ b/rpc/pbrpc.pro @@ -2,7 +2,6 @@ TEMPLATE = lib CONFIG += qt staticlib QT += network DEFINES += HAVE_REMOTE -INCLUDEPATH += "c:\msys\1.0\local\include" -LIBS += -L"C:\msys\1.0\local\lib" -lprotobuf +LIBS += -lprotobuf HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h SOURCES += rpcserver.cpp pbrpcchannel.cpp From 46cebefb19fefdc4d45a4f3fd233c482b99dca3e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 9 May 2010 10:28:58 +0530 Subject: [PATCH 072/294] IP4over4 implemented as a "combo" protocol; added IP 4over4 as a L3 protocol in StreamConfigDialog. Also temporarily masked a change introduced in r59ec which triggers a crash if you click "cancel" in StreamConfigDialog with the "Protocol Data" tab open --- client/streamconfigdialog.cpp | 4 ++ client/streamconfigdialog.ui | 17 +++++- common/comboprotocol.h | 5 +- common/ip4.ui | 24 ++------ common/ip4over4.h | 101 ++++++++++++++++++++++++++++++++++ common/ip4over4.proto | 37 +++++++++++++ common/ostproto.pro | 2 + common/protocol.proto | 1 + common/protocolmanager.cpp | 8 ++- 9 files changed, 173 insertions(+), 26 deletions(-) create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 7789f8d..6d336e3 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -200,6 +200,8 @@ void StreamConfigDialog::setupUiExtra() bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif @@ -952,8 +954,10 @@ void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) // Refresh protocol widgets in case there is any dependent data between // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it mpStream->storeProtocolWidgets(); mpStream->loadProtocolWidgets(); +#endif } void StreamConfigDialog::on_pbOk_clicked() diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index f64c848..687b970 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,7 +8,7 @@ 0 0 - 569 + 579 481 @@ -173,7 +173,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 527 + 537 243 @@ -339,6 +339,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + false + + + IP 4over4 + + + false + + + + false diff --git a/common/comboprotocol.h b/common/comboprotocol.h index c6efa0a..c7d4053 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -25,7 +25,7 @@ along with this program. If not, see template class ComboProtocol : public AbstractProtocol { -private: +protected: ProtoA *protoA; ProtoB *protoB; QWidget *configForm; @@ -82,8 +82,7 @@ public: // NOTE: To use protoX->protoDataCopyFrom() we need to arrange // so that it sees its own protocolNumber() - but since the - // input param 'protocol' is 'const', we make a copy first - + // input param 'protocol' is 'const', we work on a copy proto.CopyFrom(protocol); proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); diff --git a/common/ip4.ui b/common/ip4.ui index a3135e7..c457a2d 100644 --- a/common/ip4.ui +++ b/common/ip4.ui @@ -5,8 +5,8 @@ 0 0 - 527 - 296 + 493 + 308 @@ -35,7 +35,8 @@ - Override Header Length (x4) + Override Header +Length (x4) @@ -66,16 +67,6 @@ - - - - false - - - ... - - - @@ -106,13 +97,6 @@ - - - - Qt::Vertical - - - diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..cf6c5f5 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,101 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return Ip4over4Combo::protocolFrameCksum(streamIndex, cksumType); + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..df25a32 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 132; +} diff --git a/common/ostproto.pro b/common/ostproto.pro index 33567f8..89510ce 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -34,6 +34,7 @@ PROTOS += \ vlanstack.proto \ arp.proto \ ip4.proto \ + ip4over4.proto \ icmp.proto \ tcp.proto \ udp.proto \ @@ -60,6 +61,7 @@ HEADERS += \ vlanstack.h \ arp.h \ ip4.h \ + ip4over4.h \ icmp.h \ tcp.h \ udp.h \ diff --git a/common/protocol.proto b/common/protocol.proto index bafbe8a..6f88c78 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -101,6 +101,7 @@ message Protocol { kIp4FieldNumber = 130; kArpFieldNumber = 131; + kIp4over4FieldNumber = 132; kTcpFieldNumber = 140; kUdpFieldNumber = 141; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index b543ac0..e8e6570 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -33,6 +33,7 @@ along with this program. If not, see #include "vlanstack.h" #include "arp.h" #include "ip4.h" +#include "ip4over4.h" #include "icmp.h" #include "tcp.h" #include "udp.h" @@ -49,6 +50,7 @@ ProtocolManager::ProtocolManager() */ registerProtocol(OstProto::Protocol::kMacFieldNumber, (void*) MacProtocol::createInstance); + registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kDot3FieldNumber, @@ -61,23 +63,27 @@ ProtocolManager::ProtocolManager() (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackProtocol::createInstance); + registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); From ed13bba83be854f8326bddf870b4afcd46e40d24 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 16 May 2010 11:46:41 +0530 Subject: [PATCH 073/294] Fixed Segfault caused by uninitialized ModelTester pointers in Release mode (Fixes issue 5) --- client/portgrouplist.cpp | 7 +++++-- client/streamconfigdialog.cpp | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp index a8e688c..cfdc74b 100644 --- a/client/portgrouplist.cpp +++ b/client/portgrouplist.cpp @@ -29,8 +29,11 @@ PortGroupList::PortGroupList() { PortGroup *pg; -#ifndef QT_NO_DEBUG - // TODO(LOW): Remove +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else streamModelTester_ = new ModelTest(getStreamModel()); portModelTester_ = new ModelTest(getPortModel()); portStatsModelTester_ = new ModelTest(getPortStatsModel()); diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 6d336e3..2171701 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -136,7 +136,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, LoadCurrentStream(); mpPacketModel = new PacketModel(this); tvPacketTree->setModel(mpPacketModel); -#ifndef QT_NO_DEBUG +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else mpPacketModelTester = new ModelTest(mpPacketModel); #endif tvPacketTree->header()->hide(); From 4a70d52e3e03a0b7fce1e8e58c2e317163dbec0c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 22 May 2010 22:02:46 +0530 Subject: [PATCH 074/294] Implemented IPv6 protocol; found and fixed a bug in AbstractProtocol::protocolFrameValue() in the process --- client/streamconfigdialog.cpp | 5 +- common/abstractprotocol.cpp | 13 +- common/ip6.cpp | 939 ++++++++++++++++++++++++++++++++++ common/ip6.h | 130 +++++ common/ip6.proto | 61 +++ common/ip6.ui | 467 +++++++++++++++++ common/ipv6addressvalidator.h | 75 +++ common/ostproto.pro | 4 + common/protocol.proto | 1 + common/protocolmanager.cpp | 3 + 10 files changed, 1691 insertions(+), 7 deletions(-) create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ipv6addressvalidator.h diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 2171701..e4544e5 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -147,8 +147,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // TODO(MED): - //! \todo Implement then enable these protocols - IPv6, IGMP - rbL3Ipv6->setHidden(true); + //! \todo Implement then enable these protocols - IGMP rbL4Igmp->setHidden(true); //! \todo Enable navigation of streams pbPrev->setDisabled(true); @@ -200,7 +199,7 @@ void StreamConfigDialog::setupUiExtra() #else bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); - bgProto[ProtoL3]->addButton(rbL3Ipv6, 0xFFFF); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip4over4, OstProto::Protocol::kIp4over4FieldNumber); diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 8ed7513..0dc3178 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -249,7 +249,7 @@ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const \note If a subclass uses any of the below functions to derive FieldFrameValue, the subclass should handle and return a value for - FieldBitSize to prevent endless recrusion - + FieldBitSize to prevent endless recursion - - protocolFrameCksum() - protocolFramePayloadSize() */ @@ -454,7 +454,10 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) } else field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); - qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size()); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < (%db/%dB) %s >", bits, field.size(), + QString(field.toHex()).toAscii().constData()); if (bits == (uint) field.size() * 8) { @@ -465,10 +468,12 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) Q_ASSERT(field.size() > 0); char c = proto[proto.size() - 1]; - proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos); + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); for (int j = 0; j < field.size() - 1; j++) proto.append(field.at(j) << lastbitpos | - field.at(j+1) >> lastbitpos); + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); } } else if (bits < (uint) field.size() * 8) diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..ea1f22c --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,939 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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 |= FieldIsMeta; + 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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi, hostLo; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi, hostLo; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (addr.at(i) << 8) + addr.at(i+1); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (addr.at(i) << 8) + addr.at(i+1); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..1856bc8 --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..f47f26a --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 133; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..4a0aae2 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int &pos) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 89510ce..8a4c65b 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -13,6 +13,7 @@ FORMS += \ vlan.ui \ arp.ui \ ip4.ui \ + ip6.ui \ icmp.ui \ tcp.ui \ udp.ui \ @@ -34,6 +35,7 @@ PROTOS += \ vlanstack.proto \ arp.proto \ ip4.proto \ + ip6.proto \ ip4over4.proto \ icmp.proto \ tcp.proto \ @@ -61,6 +63,7 @@ HEADERS += \ vlanstack.h \ arp.h \ ip4.h \ + ip6.h \ ip4over4.h \ icmp.h \ tcp.h \ @@ -84,6 +87,7 @@ SOURCES += \ svlan.cpp \ arp.cpp \ ip4.cpp \ + ip6.cpp \ icmp.cpp \ tcp.cpp \ udp.cpp \ diff --git a/common/protocol.proto b/common/protocol.proto index 6f88c78..dddc087 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -102,6 +102,7 @@ message Protocol { kIp4FieldNumber = 130; kArpFieldNumber = 131; kIp4over4FieldNumber = 132; + kIp6FieldNumber = 133; kTcpFieldNumber = 140; kUdpFieldNumber = 141; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index e8e6570..64a537a 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -33,6 +33,7 @@ along with this program. If not, see #include "vlanstack.h" #include "arp.h" #include "ip4.h" +#include "ip6.h" #include "ip4over4.h" #include "icmp.h" #include "tcp.h" @@ -75,6 +76,8 @@ ProtocolManager::ProtocolManager() (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4Protocol::createInstance); From 956455137cfdbc41e4ec65d453fb0869aba7d115 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 May 2010 16:46:43 +0530 Subject: [PATCH 075/294] Implemented the remaining IP Tunneling protocols - IP6over4, IP4over6, IP6over6; renumbered L3 protocol protobuf "field numbers". ComboProtocol now implements PseudoIp cksum as protoB's cksum as a convenience for the IP tunneling protocols. Also found and fixed an issue with IPv6's pseudoIp cksum which was causing the TCP/UDP cksums to be incorrect --- client/streamconfigdialog.cpp | 8 ++- client/streamconfigdialog.ui | 85 +++++++++++++++++++++++--------- common/arp.proto | 2 +- common/comboprotocol.h | 12 ++++- common/ip4.proto | 2 +- common/ip4over4.h | 10 ---- common/ip4over4.proto | 2 +- common/ip4over6.h | 30 ++++++++++++ common/ip4over6.proto | 31 ++++++++++++ common/ip6.cpp | 4 +- common/ip6.proto | 2 +- common/ip6over4.h | 30 ++++++++++++ common/ip6over4.proto | 31 ++++++++++++ common/ip6over6.h | 91 +++++++++++++++++++++++++++++++++++ common/ip6over6.proto | 37 ++++++++++++++ common/ostproto.pro | 6 +++ common/protocol.proto | 11 +++-- common/protocolmanager.cpp | 9 ++++ 18 files changed, 357 insertions(+), 46 deletions(-) create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index e4544e5..993cd1c 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -198,11 +198,17 @@ void StreamConfigDialog::setupUiExtra() bgProto[ProtoL3]->addButton(btn); #else bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); - bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip4over4, OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 687b970..43c56b6 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,8 +8,8 @@ 0 0 - 579 - 481 + 626 + 507 @@ -173,8 +173,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 537 - 243 + 584 + 269 @@ -306,6 +306,16 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + false + + + ARP + + + + false @@ -318,7 +328,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + false @@ -328,17 +338,33 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - + + false - ARP + IP 6over4 + + + false - + + + + false + + + IP 4over6 + + + false + + + + false @@ -351,7 +377,20 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + + + + false + + + IP 6over6 + + + false + + + + false @@ -520,19 +559,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Qt::Vertical - - - - 20 - 71 - - - - @@ -575,6 +601,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/common/arp.proto b/common/arp.proto index 9a4bd52..c4a60e4 100644 --- a/common/arp.proto +++ b/common/arp.proto @@ -63,5 +63,5 @@ message Arp { } extend Protocol { - optional Arp arp = 131; + optional Arp arp = 130; } diff --git a/common/comboprotocol.h b/common/comboprotocol.h index c7d4053..29cf71d 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -170,9 +170,17 @@ public: || protoB->isProtocolFrameSizeVariable()); } -#if 0 virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const; + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 quint32 protocolFrameHeaderCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, diff --git a/common/ip4.proto b/common/ip4.proto index 9914977..6014321 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -61,5 +61,5 @@ message Ip4 { } extend Protocol { - optional Ip4 ip4 = 130; + optional Ip4 ip4 = 131; } diff --git a/common/ip4over4.h b/common/ip4over4.h index cf6c5f5..9ca1be7 100644 --- a/common/ip4over4.h +++ b/common/ip4over4.h @@ -86,16 +86,6 @@ public: protoB->protoDataCopyFrom(tempProto); } } - virtual quint32 protocolFrameCksum(int streamIndex = 0, - CksumType cksumType = CksumIp) const - { - // For a Pseudo IP cksum, we assume it is the succeeding protocol - // that is requesting it and hence return protoB's cksum; - if (cksumType == CksumIpPseudo) - return protoB->protocolFrameCksum(streamIndex, cksumType); - - return Ip4over4Combo::protocolFrameCksum(streamIndex, cksumType); - } }; #endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto index df25a32..3da405a 100644 --- a/common/ip4over4.proto +++ b/common/ip4over4.proto @@ -33,5 +33,5 @@ extend Ip4over4 { } extend Protocol { - optional Ip4over4 ip4over4 = 132; + optional Ip4over4 ip4over4 = 135; } diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..11d26e4 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 134; +} diff --git a/common/ip6.cpp b/common/ip6.cpp index ea1f22c..8ac6fca 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -819,13 +819,13 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, .toByteArray(); Q_ASSERT(addr.size() == 16); for (int i = 0; i < addr.size(); i+=2) - sum += (addr.at(i) << 8) + addr.at(i+1); + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) .toByteArray(); Q_ASSERT(addr.size() == 16); for (int i = 0; i < addr.size(); i+=2) - sum += (addr.at(i) << 8) + addr.at(i+1); + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) .toUInt() & 0xFFFF; diff --git a/common/ip6.proto b/common/ip6.proto index f47f26a..aed96f2 100644 --- a/common/ip6.proto +++ b/common/ip6.proto @@ -57,5 +57,5 @@ message Ip6 { } extend Protocol { - optional Ip6 ip6 = 133; + optional Ip6 ip6 = 132; } diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..47fee75 --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 133; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f260916 --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 136; +} diff --git a/common/ostproto.pro b/common/ostproto.pro index 8a4c65b..e16b4b3 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -36,7 +36,10 @@ PROTOS += \ arp.proto \ ip4.proto \ ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ ip4over4.proto \ + ip6over6.proto \ icmp.proto \ tcp.proto \ udp.proto \ @@ -64,7 +67,10 @@ HEADERS += \ arp.h \ ip4.h \ ip6.h \ + ip6over4.h \ + ip4over6.h \ ip4over4.h \ + ip6over6.h \ icmp.h \ tcp.h \ udp.h \ diff --git a/common/protocol.proto b/common/protocol.proto index dddc087..2a5502a 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -99,10 +99,13 @@ message Protocol { kDot2SnapFieldNumber = 128; kVlanStackFieldNumber = 129; - kIp4FieldNumber = 130; - kArpFieldNumber = 131; - kIp4over4FieldNumber = 132; - kIp6FieldNumber = 133; + kArpFieldNumber = 130; + kIp4FieldNumber = 131; + kIp6FieldNumber = 132; + kIp6over4FieldNumber = 133; + kIp4over6FieldNumber = 134; + kIp4over4FieldNumber = 135; + kIp6over6FieldNumber = 136; kTcpFieldNumber = 140; kUdpFieldNumber = 141; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 64a537a..0910e1a 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -34,7 +34,10 @@ along with this program. If not, see #include "arp.h" #include "ip4.h" #include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" #include "ip4over4.h" +#include "ip6over6.h" #include "icmp.h" #include "tcp.h" #include "udp.h" @@ -78,8 +81,14 @@ ProtocolManager::ProtocolManager() (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); From 7327fbb58d6bbbe8f4bd4a7a61b298b9dd003088 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 May 2010 22:30:32 +0530 Subject: [PATCH 076/294] Fixed a bug in IPv4 pseudoIP cksum calculation which would cause invalid TCP and UDP cksums in certain cases --- common/ip4.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/ip4.cpp b/common/ip4.cpp index f3bb887..54d069f 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -622,6 +622,9 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + // Above calculation done assuming 'big endian' // - so convert to host order //return qFromBigEndian(sum); From 4cba17e6c8212fcfdb3a243d1da19b98145b687f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 24 May 2010 21:49:59 +0530 Subject: [PATCH 077/294] Fixed compiler warning --- common/ipv6addressvalidator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h index 4a0aae2..ffbd7d5 100644 --- a/common/ipv6addressvalidator.h +++ b/common/ipv6addressvalidator.h @@ -33,7 +33,7 @@ public: } ~IPv6AddressValidator() {} - virtual QValidator::State validate(QString &input, int &pos) const + virtual QValidator::State validate(QString &input, int& /*pos*/) const { QValidator::State state; QHostAddress addr(input); From 4d83432a5d6246241b27528a716f97e51aec8fed Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 3 Jun 2010 20:52:49 +0530 Subject: [PATCH 078/294] Replaced top-level Makefile with a qmake .pro so the top-level Makefile is also generated now; having a top-level .pro makes it easier for folks who use Qt Creator. Also 'make install' is now supported - a custom install path prefix can be provided, if required, by passing PREFIX=/absolute/path/prefix to qmake --- .hgignore | 2 +- Makefile | 30 ------------------------------ client/ostinato.pro | 1 + install.pri | 9 +++++++++ ost.pro | 7 +++++++ server/drone.pro | 1 + 6 files changed, 19 insertions(+), 31 deletions(-) delete mode 100644 Makefile create mode 100644 install.pri create mode 100644 ost.pro diff --git a/.hgignore b/.hgignore index b0a474f..e27298d 100644 --- a/.hgignore +++ b/.hgignore @@ -11,7 +11,7 @@ moc_*.cpp qrc_*.cpp # QMake generated files -*\Makefile* +Makefile* *\object_script.* # protobuf generated files diff --git a/Makefile b/Makefile deleted file mode 100644 index 0b49aa8..0000000 --- a/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -debug: QMAKE_CONFIG=-config debug -release: QMAKE_CONFIG=-config release - -all: - $(MAKE) -C rpc - $(MAKE) -C common - $(MAKE) -C server - $(MAKE) -C client - -release: qmake all -debug: qmake all - -clean: - -$(MAKE) -C client $@ - -$(MAKE) -C server $@ - -$(MAKE) -C common $@ - -$(MAKE) -C rpc $@ - -distclean: - -$(MAKE) -C client $@ - -$(MAKE) -C server $@ - -$(MAKE) -C common $@ - -$(MAKE) -C rpc $@ - -qmake: - cd rpc && qmake $(QMAKE_CONFIG) && cd .. - cd common && qmake $(QMAKE_CONFIG) && cd .. - cd server && qmake $(QMAKE_CONFIG) && cd .. - cd client && qmake $(QMAKE_CONFIG) && cd .. - diff --git a/client/ostinato.pro b/client/ostinato.pro index 3655802..7ab270d 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -75,6 +75,7 @@ SOURCES += \ QMAKE_DISTCLEAN += object_script.* +include(../install.pri) include(../version.pri) # TODO(LOW): Test only diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..649c98e --- /dev/null +++ b/install.pri @@ -0,0 +1,9 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + win32:PREFIX = "../" +} +target.path = $$PREFIX/bin + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..bfe4e1a --- /dev/null +++ b/ost.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/server/drone.pro b/server/drone.pro index 34ea026..b2b25d1 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -40,4 +40,5 @@ SOURCES += pcapextra.cpp QMAKE_DISTCLEAN += object_script.* +include (../install.pri) include (../version.pri) From 11acbf201db0d945e354995288f9698aa8377ddd Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jun 2010 22:42:19 +0530 Subject: [PATCH 079/294] Modified ICMP protocol such that the 'Id' and 'Seq' fields are shown and used only for those ICMP types that actually have those fields --- client/packetmodel.cpp | 5 +++ common/abstractprotocol.h | 2 +- common/icmp.cpp | 67 +++++++++++++++++++++++++++++++-------- common/icmp.h | 13 +++++--- common/icmp.ui | 66 ++++++++++++++++++++++---------------- 5 files changed, 107 insertions(+), 46 deletions(-) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index b095c45..4fabe9b 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -41,6 +41,11 @@ void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) mSelectedProtocols = currentProtocols; reset(); } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } } int PacketModel::rowCount(const QModelIndex &parent) const diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index a36bbef..ac3c863 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -119,7 +119,7 @@ public: virtual int fieldCount() const; int metaFieldCount() const; - int frameFieldCount() const; + virtual int frameFieldCount() const; virtual FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/icmp.cpp b/common/icmp.cpp index 67290c4..2cf08c9 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -17,32 +17,61 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include #include "icmp.h" +#include +#include + +const int kIcmpEchoReply = 0; +const int kIcmpDestinationUnreachable = 3; +const int kIcmpSourceQuench = 4; +const int kIcmpRedirect = 5; +const int kIcmpEchoRequest = 8; +const int kIcmpTimeExceeded = 11; +const int kIcmpParameterProblem = 12; +const int kIcmpTimestampRequest = 13; +const int kIcmpTimestampReply = 14; +const int kIcmpInformationRequest = 15; +const int kIcmpInformationReply = 16; +const int kIcmpAddressMaskRequest = 17; +const int kIcmpAddressMaskReply = 18; + +static QSet idSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + IcmpConfigForm::IcmpConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); - typeCombo->addItem(0, "Echo Reply"); - typeCombo->addItem(3, "Destination Unreachable"); - typeCombo->addItem(4, "Source Quench"); - typeCombo->addItem(5, "Redirect"); - typeCombo->addItem(8, "Echo Request"); - typeCombo->addItem(11, "Time Exceeded"); - typeCombo->addItem(12, "Parameter Problem"); - typeCombo->addItem(13, "Timestamp Request"); - typeCombo->addItem(14, "Timestamp Reply"); - typeCombo->addItem(17, "Address Mask Request"); - typeCombo->addItem(18, "Address Mask Reply"); + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); } +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible(idSeqSet.contains(typeCombo->currentValue())); +} + IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { @@ -104,6 +133,16 @@ int IcmpProtocol::fieldCount() const return icmp_fieldCount; } +int IcmpProtocol::frameFieldCount() const +{ + int count = AbstractProtocol::frameFieldCount(); + + if (!idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) + count -=2; + + return count; +} + AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; @@ -122,6 +161,8 @@ AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const case icmp_identifier: case icmp_sequence: + if (!idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) + flags |= FieldIsMeta; break; case icmp_is_override_checksum: @@ -169,7 +210,7 @@ QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("code"); + return QString("Code"); case FieldValue: return code; case FieldTextValue: diff --git a/common/icmp.h b/common/icmp.h index 0d6f218..ad72432 100644 --- a/common/icmp.h +++ b/common/icmp.h @@ -27,11 +27,12 @@ along with this program. If not, see /* Icmp Protocol Frame Format - - +-----+------+------+-----+-----+ - | TYP | CODE | CSUM | ID | SEQ | - | (1) | (1) | (2) | (2) | (2) | - +-----+------+------+-----+-----+ -Figures in brackets represent field width in bytes + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes */ class IcmpConfigForm : public QWidget, public Ui::Icmp @@ -40,6 +41,7 @@ class IcmpConfigForm : public QWidget, public Ui::Icmp public: IcmpConfigForm(QWidget *parent = 0); private slots: + void on_typeCombo_currentIndexChanged(int index); }; class IcmpProtocol : public AbstractProtocol @@ -79,6 +81,7 @@ public: virtual QString shortName() const; virtual int fieldCount() const; + virtual int frameFieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/icmp.ui b/common/icmp.ui index bf54421..6e62349 100644 --- a/common/icmp.ui +++ b/common/icmp.ui @@ -5,8 +5,8 @@ 0 0 - 258 - 168 + 291 + 190 @@ -66,41 +66,53 @@ - - - - Identifier + + + + QFrame::StyledPanel - - idEdit + + QFrame::Plain + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + - - - - - - - Sequence - - - seqEdit - - - - - - - + Qt::Vertical - 20 - 40 + 137 + 16 From 710eb7f836110c57129aae194727cf714a1514a5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 7 Jun 2010 19:20:35 +0530 Subject: [PATCH 080/294] Fixed a bug in IcmpProtocol::frameFieldCount() which would return incorrect values sometimes --- common/icmp.cpp | 11 +++++++---- common/icmp.h | 7 +++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/common/icmp.cpp b/common/icmp.cpp index 2cf08c9..0529438 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -135,12 +135,15 @@ int IcmpProtocol::fieldCount() const int IcmpProtocol::frameFieldCount() const { - int count = AbstractProtocol::frameFieldCount(); + int count; + + if (idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; - if (!idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) - count -=2; - return count; + } AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const diff --git a/common/icmp.h b/common/icmp.h index ad72432..7433a5a 100644 --- a/common/icmp.h +++ b/common/icmp.h @@ -55,11 +55,14 @@ private: icmp_type = 0, icmp_code, icmp_checksum, - icmp_identifier, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, icmp_sequence, + icmp_idSeqFrameFieldCount, // Meta Fields - icmp_is_override_checksum, + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, icmp_fieldCount }; From 069cc162797cbdd9508c652d02048a57d203b373 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 7 Jun 2010 20:52:50 +0530 Subject: [PATCH 081/294] Protocol Framework Change: FieldFlags - FrameField and MetaField are no longer exclusive. A field can be neither. Also frameFieldCount() now actually counts the number of frameFields instead of deriving it from metaFieldCount() - these changes allow protocols to export different sets of fields based on a opcode/type field. --- client/packetmodel.cpp | 3 +-- common/abstractprotocol.cpp | 45 ++++++++++++++++++++++++------------- common/abstractprotocol.h | 9 ++++---- common/arp.cpp | 3 ++- common/icmp.cpp | 7 +++--- common/ip4.cpp | 5 +++-- common/ip6.cpp | 3 ++- common/mac.cpp | 3 ++- common/payload.cpp | 3 ++- common/sample.cpp | 19 ++++++++++++++-- common/sample.h | 1 + common/tcp.cpp | 5 +++-- common/textproto.cpp | 3 ++- common/udp.cpp | 5 +++-- common/vlan.cpp | 3 ++- 15 files changed, 79 insertions(+), 38 deletions(-) diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp index 4fabe9b..ecb4196 100644 --- a/client/packetmodel.cpp +++ b/client/packetmodel.cpp @@ -172,8 +172,7 @@ QVariant PacketModel::data(const QModelIndex &index, int role) const while (n) { - if (!(p->fieldFlags(fieldIdx).testFlag( - AbstractProtocol::FieldIsMeta))) + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) n--; fieldIdx++; } diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 0dc3178..522605b 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -76,7 +76,8 @@ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) mpStream = stream; this->parent = parent; prev = next = NULL; - metaCount = -1; + _metaFieldCount = -1; + _frameFieldCount = -1; protoSize = -1; } @@ -184,45 +185,59 @@ int AbstractProtocol::fieldCount() const Returns the number of meta fields The default implementation counts and returns the number of fields for which - the FieldIsMeta flag is set\n + the MetaField flag is set\n The default implementation caches the count on its first invocation and subsequently returns the cached count */ int AbstractProtocol::metaFieldCount() const { - if (metaCount < 0) + if (_metaFieldCount < 0) { int c = 0; for (int i = 0; i < fieldCount() ; i++) - if (fieldFlags(i).testFlag(FieldIsMeta)) + if (fieldFlags(i).testFlag(MetaField)) c++; - metaCount = c; + _metaFieldCount = c; } - return metaCount; + return _metaFieldCount; } /*! Returns the number of frame fields - Convenience method - same as fieldCount() minus metaFieldCount() + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function */ int AbstractProtocol::frameFieldCount() const { - //qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount()); - return (fieldCount() - metaFieldCount()); + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; } /*! Returns the field flags for the passed in field index The default implementation assumes all fields to be frame fields and returns - 'FieldIsNormal'. Subclasses must reimplement this method if they have any + 'FrameField'. Subclasses must reimplement this method if they have any meta fields or checksum fields. See the SampleProtocol for an example. */ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const { - return FieldIsNormal; + return FrameField; } /*! @@ -261,7 +276,7 @@ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString(); case FieldBitSize: - Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum), + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), "AbstractProtocol::fieldData()", "FieldBitSize for checksum fields need to be handled by the subclass"); return fieldData(index, FieldFrameValue, streamIndex). @@ -366,7 +381,7 @@ int AbstractProtocol::protocolFrameSize(int streamIndex) const for (int i = 0; i < fieldCount(); i++) { - if (!fieldFlags(i).testFlag(FieldIsMeta)) + if (fieldFlags(i).testFlag(FrameField)) bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); } protoSize = (bitsize+7)/8; @@ -440,14 +455,14 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) for (int i=0; i < fieldCount() ; i++) { flags = fieldFlags(i); - if (!flags.testFlag(FieldIsMeta)) + if (flags.testFlag(FrameField)) { bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); if (bits == 0) continue; Q_ASSERT(bits > 0); - if (forCksum && flags.testFlag(FieldIsCksum)) + if (forCksum && flags.testFlag(CksumField)) { field.resize((bits+7)/8); field.fill('\0'); diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index ac3c863..053e303 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -48,7 +48,8 @@ class AbstractProtocol friend class ProtocolListIterator; private: - mutable int metaCount; + mutable int _metaFieldCount; + mutable int _frameFieldCount; mutable int protoSize; mutable QString protoAbbr; @@ -61,9 +62,9 @@ protected: public: //! Properties of a field, can be OR'd enum FieldFlag { - FieldIsNormal = 0x0, //!< field appears in frame content - FieldIsMeta = 0x1, //!< field does not appear in frame, is meta data - FieldIsCksum = 0x2 //!< field is a checksum, appears in frame content + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content }; Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd diff --git a/common/arp.cpp b/common/arp.cpp index 84a6094..844a2d7 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -203,7 +203,8 @@ AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const case arp_targetProtoAddrMode: case arp_targetProtoAddrCount: case arp_targetProtoAddrMask: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/icmp.cpp b/common/icmp.cpp index 0529438..4bdc73d 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -159,17 +159,18 @@ AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const break; case icmp_checksum: - flags |= FieldIsCksum; + flags |= CksumField; break; case icmp_identifier: case icmp_sequence: if (!idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) - flags |= FieldIsMeta; + flags &= ~FrameField; break; case icmp_is_override_checksum: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/ip4.cpp b/common/ip4.cpp index 54d069f..776752f 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -156,7 +156,7 @@ AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const break; case ip4_cksum: - flags |= FieldIsCksum; + flags |= CksumField; break; case ip4_srcAddr: @@ -173,7 +173,8 @@ AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const case ip4_dstAddrMode: case ip4_dstAddrCount: case ip4_dstAddrMask: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/ip6.cpp b/common/ip6.cpp index 8ac6fca..f8b7555 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -165,7 +165,8 @@ AbstractProtocol::FieldFlags Ip6Protocol::fieldFlags(int index) const case ip6_dstAddrMode: case ip6_dstAddrCount: case ip6_dstAddrPrefix: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/mac.cpp b/common/mac.cpp index 706931a..040f870 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -136,7 +136,8 @@ AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const case mac_srcMacMode: case mac_srcMacCount: case mac_srcMacStep: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; } diff --git a/common/payload.cpp b/common/payload.cpp index 4536a82..d5fb2e5 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -126,7 +126,8 @@ AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const // Meta fields case payload_dataPatternMode: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; } diff --git a/common/sample.cpp b/common/sample.cpp index d83ab54..2a79660 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -109,6 +109,20 @@ int SampleProtocol::fieldCount() const return sample_fieldCount; } +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + /*! TODO Edit this function to return the appropriate flags for each field \n @@ -128,7 +142,7 @@ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const break; case sample_checksum: - flags |= FieldIsCksum; + flags |= CksumField; break; case sample_x: @@ -136,7 +150,8 @@ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const break; case sample_is_override_checksum: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/sample.h b/common/sample.h index b92152b..91e6573 100644 --- a/common/sample.h +++ b/common/sample.h @@ -81,6 +81,7 @@ public: virtual QString shortName() const; virtual int fieldCount() const; + virtual int frameFieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, diff --git a/common/tcp.cpp b/common/tcp.cpp index f29b1a1..02faa6a 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -113,7 +113,7 @@ AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const break; case tcp_cksum: - flags |= FieldIsCksum; + flags |= CksumField; break; case tcp_urg_ptr: @@ -123,7 +123,8 @@ AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const case tcp_is_override_dst_port: case tcp_is_override_hdrlen: case tcp_is_override_cksum: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/textproto.cpp b/common/textproto.cpp index 90327ea..c86f7ff 100644 --- a/common/textproto.cpp +++ b/common/textproto.cpp @@ -108,7 +108,8 @@ AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const case textProto_portNum: case textProto_encoding: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/udp.cpp b/common/udp.cpp index 4097ff8..8f3c35b 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -108,14 +108,15 @@ AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const break; case udp_cksum: - flags |= FieldIsCksum; + flags |= CksumField; break; case udp_isOverrideSrcPort: case udp_isOverrideDstPort: case udp_isOverrideTotLen: case udp_isOverrideCksum: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; default: diff --git a/common/vlan.cpp b/common/vlan.cpp index 2946068..95b2304 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -93,7 +93,8 @@ AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const // meta-fields case vlan_isOverrideTpid: - flags |= FieldIsMeta; + flags &= ~FrameField; + flags |= MetaField; break; } From fedc4ec5d10b857d4aed894e26a011a30f7212a4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 11 Jun 2010 20:48:24 +0530 Subject: [PATCH 082/294] Extended ICMP to work for ICMPv6 as well as ICMPv4 --- common/icmp.cpp | 193 ++++++++++++++++++++++++++++++++++++++-------- common/icmp.h | 17 ++++ common/icmp.proto | 18 +++-- common/icmp.ui | 94 +++++++++++++--------- 4 files changed, 249 insertions(+), 73 deletions(-) diff --git a/common/icmp.cpp b/common/icmp.cpp index 4bdc73d..e7f6267 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -23,45 +23,86 @@ along with this program. If not, see #include #include -const int kIcmpEchoReply = 0; -const int kIcmpDestinationUnreachable = 3; -const int kIcmpSourceQuench = 4; -const int kIcmpRedirect = 5; -const int kIcmpEchoRequest = 8; -const int kIcmpTimeExceeded = 11; -const int kIcmpParameterProblem = 12; -const int kIcmpTimestampRequest = 13; -const int kIcmpTimestampReply = 14; -const int kIcmpInformationRequest = 15; -const int kIcmpInformationReply = 16; -const int kIcmpAddressMaskRequest = 17; -const int kIcmpAddressMaskReply = 18; +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; -static QSet idSeqSet = QSet() +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() << kIcmpEchoRequest << kIcmpEchoReply << kIcmpInformationRequest << kIcmpInformationReply; +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + IcmpConfigForm::IcmpConfigForm(QWidget *parent) : QWidget(parent) { + versionGroup = new QButtonGroup(this); setupUi(this); + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); - typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); - typeCombo->addItem(kIcmpDestinationUnreachable, "Destination Unreachable"); - typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); - typeCombo->addItem(kIcmpRedirect, "Redirect"); - typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); - typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); - typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); - typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); - typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); - typeCombo->addItem(kIcmpInformationRequest, "Information Request"); - typeCombo->addItem(kIcmpInformationReply, "Information Reply"); - typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); - typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + + icmp4Button->click(); idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); @@ -69,7 +110,61 @@ IcmpConfigForm::IcmpConfigForm(QWidget *parent) void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) { - idSeqFrame->setVisible(idSeqSet.contains(typeCombo->currentValue())); + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); } IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) @@ -121,7 +216,13 @@ quint32 IcmpProtocol::protocolId(ProtocolIdType type) const { switch(type) { - case ProtocolIdIp: return 0x1; + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } default:break; } @@ -137,7 +238,7 @@ int IcmpProtocol::frameFieldCount() const { int count; - if (idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) + if (isIdSeqType(icmpVersion(), icmpType())) count = icmp_idSeqFrameFieldCount; else count = icmp_commonFrameFieldCount; @@ -164,10 +265,11 @@ AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const case icmp_identifier: case icmp_sequence: - if (!idSeqSet.contains(fieldData(icmp_type, FieldValue).toUInt())) + if (!isIdSeqType(icmpVersion(), icmpType())) flags &= ~FrameField; break; + case icmp_version: case icmp_is_override_checksum: flags &= ~FrameField; flags |= MetaField; @@ -249,6 +351,12 @@ QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, sum += (quint16) ~cks; cks = protocolFramePayloadCksum(streamIndex, CksumIp); sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); @@ -333,6 +441,17 @@ QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } case icmp_is_override_checksum: { switch(attrib) @@ -398,6 +517,13 @@ bool IcmpProtocol::setFieldData(int index, const QVariant &value, data.set_sequence(seq); break; } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } case icmp_is_override_checksum: { bool ovr = value.toBool(); @@ -430,6 +556,8 @@ void IcmpProtocol::loadConfigWidget() { configWidget(); + configForm->versionGroup->button(icmpVersion())->click(); + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); @@ -450,6 +578,9 @@ void IcmpProtocol::storeConfigWidget() bool isOk; configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + setFieldData(icmp_type, configForm->typeCombo->currentValue()); setFieldData(icmp_code, configForm->codeEdit->text()); diff --git a/common/icmp.h b/common/icmp.h index 7433a5a..a3fc296 100644 --- a/common/icmp.h +++ b/common/icmp.h @@ -25,6 +25,8 @@ along with this program. If not, see #include "abstractprotocol.h" +#include + /* Icmp Protocol Frame Format - +-----+------+------+------+-------+ @@ -39,9 +41,12 @@ class IcmpConfigForm : public QWidget, public Ui::Icmp { Q_OBJECT public: + QButtonGroup *versionGroup; + IcmpConfigForm(QWidget *parent = 0); private slots: void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); }; class IcmpProtocol : public AbstractProtocol @@ -63,10 +68,22 @@ private: // Meta Fields icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, icmp_fieldCount }; + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + public: IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~IcmpProtocol(); diff --git a/common/icmp.proto b/common/icmp.proto index f48e9a7..2cd6025 100644 --- a/common/icmp.proto +++ b/common/icmp.proto @@ -24,13 +24,19 @@ package OstProto; // Icmp Protocol message Icmp { - optional bool is_override_checksum = 1; + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } - optional uint32 type = 2 [default = 0x8]; // echo request - optional uint32 code = 3; - optional uint32 checksum = 4; - optional uint32 identifier = 5 [default = 1234]; - optional uint32 sequence = 6; + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; } extend Protocol { diff --git a/common/icmp.ui b/common/icmp.ui index 6e62349..7ba1938 100644 --- a/common/icmp.ui +++ b/common/icmp.ui @@ -5,15 +5,38 @@ 0 0 - 291 - 190 + 373 + 166 Form - + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + Type @@ -23,10 +46,10 @@ - + - + Code @@ -36,46 +59,43 @@ - + - - - - Checksum - - - - - - - false - - - - + Qt::Horizontal - 40 + 31 20 - - - - QFrame::StyledPanel + + + + Checksum - - QFrame::Plain + + + + + + false - - + + + + + + + + + Identifier @@ -85,10 +105,10 @@ - + - + Sequence @@ -98,21 +118,21 @@ - + - + Qt::Vertical - 137 - 16 + 211 + 71 @@ -127,6 +147,8 @@ + icmp4Button + icmp6Button typeCombo codeEdit overrideCksum From 2e6503faad581d358dc589eb109985904641abc4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 28 Jun 2010 21:10:29 +0530 Subject: [PATCH 083/294] Bumped version to 0.1.1 --- version.pri | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.pri b/version.pri index 103b6e9..236cdfa 100644 --- a/version.pri +++ b/version.pri @@ -1,7 +1,7 @@ -APP_VERSION = 0.1 +APP_VERSION = 0.1.1 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision -#APP_REVISION = +#APP_REVISION = @ APP_VERSION_FILE = version.cpp revtarget.target = $$APP_VERSION_FILE win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ From c2fac2db70ab030ae90a32bcc690d5f8f4114e9f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 13 Jul 2010 20:36:35 +0530 Subject: [PATCH 084/294] Added streams save/restore; Ostinato file format specified; Protocol Field Numbering scheme changed - existing protocol field numbers changed accordingly --- client/main.cpp | 9 +- client/port.cpp | 52 ++++- client/port.h | 5 +- client/portswindow.cpp | 152 +++++++++------ client/portswindow.h | 9 +- client/portswindow.ui | 10 + client/streammodel.cpp | 22 +++ client/streammodel.h | 2 + common/arp.proto | 2 +- common/crc32c.cpp | 134 +++++++++++++ common/crc32c.h | 23 +++ common/dot2llc.proto | 2 +- common/dot2snap.proto | 2 +- common/dot3.proto | 2 +- common/eth2.proto | 2 +- common/fileformat.cpp | 411 ++++++++++++++++++++++++++++++++++++++++ common/fileformat.h | 59 ++++++ common/fileformat.proto | 98 ++++++++++ common/icmp.proto | 2 +- common/ip4.proto | 2 +- common/ip4over4.proto | 2 +- common/ip4over6.proto | 2 +- common/ip6.proto | 2 +- common/ip6over4.proto | 2 +- common/ip6over6.proto | 2 +- common/llc.proto | 2 +- common/mac.proto | 2 +- common/ostproto.pro | 23 +-- common/payload.proto | 2 +- common/protocol.proto | 54 +++--- common/sample.proto | 2 +- common/snap.proto | 2 +- common/svlan.proto | 2 +- common/tcp.proto | 2 +- common/textproto.proto | 2 +- common/udp.proto | 2 +- common/userscript.proto | 2 +- common/vlan.proto | 2 +- common/vlanstack.proto | 2 +- protobuf.pri | 33 ++++ 40 files changed, 1008 insertions(+), 136 deletions(-) create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100644 protobuf.pri diff --git a/client/main.cpp b/client/main.cpp index 91f61d8..a13ff77 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -24,6 +24,8 @@ along with this program. If not, see #include #include +extern const char* version; +extern const char* revision; extern ProtocolManager *OstProtocolManager; QSettings *appSettings; @@ -34,6 +36,11 @@ int main(int argc, char* argv[]) QApplication app(argc, argv); int exitCode; + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + OstProtocolManager = new ProtocolManager(); /* (Portable Mode) If we have a .ini file in the same directory as the @@ -44,7 +51,7 @@ int main(int argc, char* argv[]) if (QFile::exists(portableIni)) appSettings = new QSettings(portableIni, QSettings::IniFormat); else - appSettings = new QSettings("Ostinato", "Ostinato"); + appSettings = new QSettings(); mainWindow = new MainWindow; mainWindow->show(); diff --git a/client/port.cpp b/client/port.cpp index eda09f6..6b5e13e 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -17,12 +17,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - -#include - #include "port.h" -#include "pbhelper.h" + +#include "fileformat.h" + +#include +#include +#include +#include uint Port::mAllocStreamId = 0; @@ -218,3 +220,43 @@ void Port::updateStats(OstProto::PortStats *portStats) } } +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + OstProto::StreamConfigList streams; + + if (!fileFormat.openStreams(fileName, streams, error)) + goto _fail; + + if (!append) + { + while (numStreams()) + deleteStreamAt(0); + } + + for (int i = 0; i < streams.stream_size(); i++) + { + newStreamAt(mStreams.size()); + streamByIndex(mStreams.size()-1)->protoDataCopyFrom(streams.stream(i)); + } + + emit streamListChanged(mPortGroupId, mPortId); + + return true; + +_fail: + return false; +} + +bool Port::saveStreams(QString fileName, QString &error) +{ + OstProto::StreamConfigList streams; + + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + + return fileFormat.saveStreams(streams, fileName, error); +} diff --git a/client/port.h b/client/port.h index 91f6d8a..e4bb9c9 100644 --- a/client/port.h +++ b/client/port.h @@ -114,13 +114,16 @@ public: void getModifiedStreamsSinceLastSync( OstProto::StreamConfigList &streamConfigList); - void when_syncComplete(); void updateStats(OstProto::PortStats *portStats); + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString &error); + signals: void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); }; diff --git a/client/portswindow.cpp b/client/portswindow.cpp index ccaed5a..58070a0 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -29,6 +29,8 @@ along with this program. If not, see PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent) { + QAction *sep; + delegate = new StreamListDelegate; //slm = new StreamListModel(); //plm = new PortGroupList(); @@ -43,7 +45,7 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvStreamList->verticalHeader()->setDefaultSectionSize( tvStreamList->verticalHeader()->minimumSectionSize()); - // Populate Context Menu Actions + // Populate PortList Context Menu Actions tvPortList->addAction(actionNew_Port_Group); tvPortList->addAction(actionDelete_Port_Group); tvPortList->addAction(actionConnect_Port_Group); @@ -51,12 +53,21 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvPortList->addAction(actionExclusive_Control); + // Populate StramList Context Menu Actions tvStreamList->addAction(actionNew_Stream); tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDelete_Stream); + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions addActions(tvPortList->actions()); - QAction *sep = new QAction(this); + sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(tvStreamList->actions()); @@ -77,26 +88,24 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) this, SLOT(when_portView_currentChanged(const QModelIndex&, const QModelIndex&))); - connect( tvStreamList->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(when_streamView_currentChanged(const QModelIndex&, - const QModelIndex&))); - connect( tvStreamList->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(when_streamView_selectionChanged())); + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); -#if 0 - connect( tvPortList->selectionModel(), + connect(tvStreamList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - plm->getStreamModel(), SLOT(setCurrentPortIndex(const QModelIndex&))); -#endif + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); // Initially we don't have any ports/streams - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); - when_streamView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); //! \todo Hide the Aggregate Box till we add support frAggregate->setHidden(true); @@ -148,19 +157,6 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& current, } } -void PortsWindow::when_streamView_currentChanged(const QModelIndex& /*current*/, - const QModelIndex& /*previous*/) -{ - qDebug("stream view current changed"); - updateStreamViewActions(); -} - -void PortsWindow::when_streamView_selectionChanged() -{ - qDebug("stream view selection changed"); - updateStreamViewActions(); -} - void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { @@ -182,16 +178,6 @@ void PortsWindow::when_portModel_reset() when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } -#if 0 -void PortsWindow::updateStreamViewActions(const QModelIndex& current) -{ - if (current.isValid()) - actionDelete_Stream->setEnabled(true); - else - actionDelete_Stream->setDisabled(true); -} -#endif - void PortsWindow::updateStreamViewActions() { // For some reason hasSelection() returns true even if selection size is 0 @@ -233,6 +219,9 @@ void PortsWindow::updateStreamViewActions() actionEdit_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); } void PortsWindow::updatePortViewActions(const QModelIndex& current) @@ -406,26 +395,6 @@ void PortsWindow::on_actionExclusive_Control_triggered(bool checked) plm->portGroup(current.parent()).modifyPort(current.row(), checked); } -#if 0 -void PortsWindow::on_actionNew_Stream_triggered() -{ - qDebug("New Stream Action"); - - int row = 0; - - if (tvStreamList->currentIndex().isValid()) - row = tvStreamList->currentIndex().row(); - plm->getStreamModel()->insertRows(row, 1); -} - -void PortsWindow::on_actionDelete_Stream_triggered() -{ - qDebug("Delete Stream Action"); - if (tvStreamList->currentIndex().isValid()) - plm->getStreamModel()->removeRows(tvStreamList->currentIndex().row(), 1); -} -#endif - void PortsWindow::on_actionNew_Stream_triggered() { qDebug("New Stream Action"); @@ -477,4 +446,73 @@ void PortsWindow::on_actionDelete_Stream_triggered() qDebug("No selection"); } +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + bool append = true; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams")); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?")); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + if (!plm->port(current).openStreams(fileName, append, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams")); + if (fileName.isEmpty()) + goto _exit; + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + diff --git a/client/portswindow.h b/client/portswindow.h index 607c40a..ed93854 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -48,17 +48,13 @@ private: QString lastNewPortGroup; QAbstractItemDelegate *delegate; +private slots: void updatePortViewActions(const QModelIndex& current); - //void updateStreamViewActions(const QModelIndex& current); void updateStreamViewActions(); -private slots: void on_tvStreamList_activated(const QModelIndex & index); void when_portView_currentChanged(const QModelIndex& current, const QModelIndex& previous); - void when_streamView_currentChanged(const QModelIndex& current, - const QModelIndex& previous); - void when_streamView_selectionChanged(); void when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void when_portModel_reset(); @@ -75,6 +71,9 @@ private slots: void on_actionNew_Stream_triggered(); void on_actionEdit_Stream_triggered(); void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); }; #endif diff --git a/client/portswindow.ui b/client/portswindow.ui index 3032b79..a724c06 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -230,6 +230,16 @@ Exclusive Port Control (EXPERIMENTAL) + + + Open Streams ... + + + + + Save Streams ... + + diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 5c57cdb..19942fd 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -254,8 +254,30 @@ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) else { qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } quint16 pg = current.internalId() >> 16; mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); } reset(); } + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h index b0b41d2..9c7e1aa 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -71,6 +71,8 @@ class StreamModel : public QAbstractTableModel public slots: void setCurrentPortIndex(const QModelIndex ¤t); + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); }; #endif diff --git a/common/arp.proto b/common/arp.proto index c4a60e4..12ccebb 100644 --- a/common/arp.proto +++ b/common/arp.proto @@ -63,5 +63,5 @@ message Arp { } extend Protocol { - optional Arp arp = 130; + optional Arp arp = 300; } diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.proto b/common/dot2llc.proto index 08e857c..8223650 100644 --- a/common/dot2llc.proto +++ b/common/dot2llc.proto @@ -29,5 +29,5 @@ message Dot2Llc { } extend Protocol { - optional Dot2Llc dot2Llc = 127; + optional Dot2Llc dot2Llc = 206; } diff --git a/common/dot2snap.proto b/common/dot2snap.proto index 3af4261..d49059f 100644 --- a/common/dot2snap.proto +++ b/common/dot2snap.proto @@ -27,5 +27,5 @@ message Dot2Snap { } extend Protocol { - optional Dot2Snap dot2Snap = 128; + optional Dot2Snap dot2Snap = 207; } diff --git a/common/dot3.proto b/common/dot3.proto index e3f4344..2e4070c 100644 --- a/common/dot3.proto +++ b/common/dot3.proto @@ -27,5 +27,5 @@ message Dot3 { } extend Protocol { - optional Dot3 dot3 = 122; + optional Dot3 dot3 = 201; } diff --git a/common/eth2.proto b/common/eth2.proto index e0c52a2..72d58c8 100644 --- a/common/eth2.proto +++ b/common/eth2.proto @@ -27,5 +27,5 @@ message Eth2 { } extend Protocol { - optional Eth2 eth2 = 121; + optional Eth2 eth2 = 200; } diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..619ab37 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,411 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + Q_ASSERT(meta.data().format_version_minor() == kFileFormatVersionMinor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..d181567 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,59 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "fileformat.pb.h" + +#include +#include + +class FileFormat : public QObject +{ + Q_OBJECT +public: + FileFormat(); + ~FileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + +private: + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 1; + static const uint kFileFormatVersionRevision = 0; + + void initFileMetaData(OstProto::FileMetaData &metaData); +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/icmp.proto b/common/icmp.proto index 2cd6025..6abc686 100644 --- a/common/icmp.proto +++ b/common/icmp.proto @@ -40,5 +40,5 @@ message Icmp { } extend Protocol { - optional Icmp icmp = 142; + optional Icmp icmp = 402; } diff --git a/common/ip4.proto b/common/ip4.proto index 6014321..64407ad 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -61,5 +61,5 @@ message Ip4 { } extend Protocol { - optional Ip4 ip4 = 131; + optional Ip4 ip4 = 301; } diff --git a/common/ip4over4.proto b/common/ip4over4.proto index 3da405a..5a146c3 100644 --- a/common/ip4over4.proto +++ b/common/ip4over4.proto @@ -33,5 +33,5 @@ extend Ip4over4 { } extend Protocol { - optional Ip4over4 ip4over4 = 135; + optional Ip4over4 ip4over4 = 305; } diff --git a/common/ip4over6.proto b/common/ip4over6.proto index 11d26e4..0482045 100644 --- a/common/ip4over6.proto +++ b/common/ip4over6.proto @@ -27,5 +27,5 @@ message Ip4over6 { } extend Protocol { - optional Ip4over6 ip4over6 = 134; + optional Ip4over6 ip4over6 = 304; } diff --git a/common/ip6.proto b/common/ip6.proto index aed96f2..d4831ed 100644 --- a/common/ip6.proto +++ b/common/ip6.proto @@ -57,5 +57,5 @@ message Ip6 { } extend Protocol { - optional Ip6 ip6 = 132; + optional Ip6 ip6 = 302; } diff --git a/common/ip6over4.proto b/common/ip6over4.proto index 47fee75..b8b0afd 100644 --- a/common/ip6over4.proto +++ b/common/ip6over4.proto @@ -27,5 +27,5 @@ message Ip6over4 { } extend Protocol { - optional Ip6over4 ip6over4 = 133; + optional Ip6over4 ip6over4 = 303; } diff --git a/common/ip6over6.proto b/common/ip6over6.proto index f260916..f65f6ea 100644 --- a/common/ip6over6.proto +++ b/common/ip6over6.proto @@ -33,5 +33,5 @@ extend Ip6over6 { } extend Protocol { - optional Ip6over6 ip6over6 = 136; + optional Ip6over6 ip6over6 = 306; } diff --git a/common/llc.proto b/common/llc.proto index 1bb6324..e0681aa 100644 --- a/common/llc.proto +++ b/common/llc.proto @@ -28,5 +28,5 @@ message Llc { } extend Protocol { - optional Llc llc = 123; + optional Llc llc = 202; } diff --git a/common/mac.proto b/common/mac.proto index 475590d..2055223 100644 --- a/common/mac.proto +++ b/common/mac.proto @@ -44,5 +44,5 @@ message Mac { } extend Protocol { - optional Mac mac = 51; + optional Mac mac = 100; } diff --git a/common/ostproto.pro b/common/ostproto.pro index e16b4b3..68f1cc6 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -22,6 +22,7 @@ FORMS += \ sample.ui PROTOS += \ protocol.proto \ + fileformat.proto \ mac.proto \ payload.proto \ eth2.proto \ @@ -45,10 +46,11 @@ PROTOS += \ udp.proto \ textproto.proto \ userscript.proto \ - sample.proto + sample.proto HEADERS += \ abstractprotocol.h \ comboprotocol.h \ + fileformat.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ @@ -79,6 +81,8 @@ HEADERS += \ sample.h SOURCES += \ abstractprotocol.cpp \ + crc32c.cpp \ + fileformat.cpp \ protocolmanager.cpp \ protocollist.cpp \ protocollistiterator.cpp \ @@ -101,19 +105,6 @@ SOURCES += \ userscript.cpp \ sample.cpp -protobuf_decl.name = protobuf header -protobuf_decl.input = PROTOS -protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h -protobuf_decl.commands = protoc --cpp_out="." ${QMAKE_FILE_NAME} -protobuf_decl.variable_out = GENERATED_FILES -QMAKE_EXTRA_COMPILERS += protobuf_decl - -protobuf_impl.name = protobuf implementation -protobuf_impl.input = PROTOS -protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc -protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h -protobuf_impl.commands = $$escape_expand(\n) -protobuf_impl.variable_out = GENERATED_SOURCES -QMAKE_EXTRA_COMPILERS += protobuf_impl - QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/payload.proto b/common/payload.proto index 02cfc04..bafa4c3 100644 --- a/common/payload.proto +++ b/common/payload.proto @@ -37,5 +37,5 @@ message Payload { } extend Protocol { - optional Payload payload = 52; + optional Payload payload = 101; } diff --git a/common/protocol.proto b/common/protocol.proto index 2a5502a..3510e2b 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -78,41 +78,41 @@ message Protocol { required ProtocolId protocol_id = 1; - extensions 51 to 100; // Reserved for Ostinato Use - extensions 101 to 200; // Available for use by protocols + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols enum k { - kMacFieldNumber = 51; - kPayloadFieldNumber = 52; - kSampleFieldNumber = 53; - kUserScriptFieldNumber = 54; + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; - kEth2FieldNumber = 121; - kDot3FieldNumber = 122; - kLlcFieldNumber = 123; - kSnapFieldNumber = 124; + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; - kSvlanFieldNumber = 125; - kVlanFieldNumber = 126; + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; - kDot2LlcFieldNumber = 127; - kDot2SnapFieldNumber = 128; - kVlanStackFieldNumber = 129; + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; - kArpFieldNumber = 130; - kIp4FieldNumber = 131; - kIp6FieldNumber = 132; - kIp6over4FieldNumber = 133; - kIp4over6FieldNumber = 134; - kIp4over4FieldNumber = 135; - kIp6over6FieldNumber = 136; + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; - kTcpFieldNumber = 140; - kUdpFieldNumber = 141; - kIcmpFieldNumber = 142; - kIgmpFieldNumber = 143; + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; - kTextProtocolFieldNumber = 150; + kTextProtocolFieldNumber = 500; } } diff --git a/common/sample.proto b/common/sample.proto index 6d74304..eeebfda 100644 --- a/common/sample.proto +++ b/common/sample.proto @@ -34,5 +34,5 @@ message Sample { } extend Protocol { - optional Sample sample = 53; + optional Sample sample = 102; } diff --git a/common/snap.proto b/common/snap.proto index bf54803..1354100 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -27,5 +27,5 @@ message Snap { } extend Protocol { - optional Snap snap = 124; + optional Snap snap = 203; } diff --git a/common/svlan.proto b/common/svlan.proto index aa02f70..937a9c1 100644 --- a/common/svlan.proto +++ b/common/svlan.proto @@ -23,5 +23,5 @@ import "vlan.proto"; package OstProto; extend Protocol { - optional Vlan svlan = 125; + optional Vlan svlan = 204; } diff --git a/common/tcp.proto b/common/tcp.proto index 3aa4382..93bd762 100644 --- a/common/tcp.proto +++ b/common/tcp.proto @@ -42,6 +42,6 @@ message Tcp { } extend Protocol { - optional Tcp tcp = 140; + optional Tcp tcp = 400; } diff --git a/common/textproto.proto b/common/textproto.proto index 8bb290b..12e5b4e 100644 --- a/common/textproto.proto +++ b/common/textproto.proto @@ -33,5 +33,5 @@ message TextProtocol { } extend Protocol { - optional TextProtocol textProtocol = 150; + optional TextProtocol textProtocol = 500; } diff --git a/common/udp.proto b/common/udp.proto index 8852c11..802135e 100644 --- a/common/udp.proto +++ b/common/udp.proto @@ -35,5 +35,5 @@ message Udp { } extend Protocol { - optional Udp udp = 141; + optional Udp udp = 401; } diff --git a/common/userscript.proto b/common/userscript.proto index 484223f..aa1e195 100644 --- a/common/userscript.proto +++ b/common/userscript.proto @@ -29,5 +29,5 @@ message UserScript { } extend Protocol { - optional UserScript userScript = 54; + optional UserScript userScript = 103; } diff --git a/common/vlan.proto b/common/vlan.proto index 802627c..0bfc2a0 100644 --- a/common/vlan.proto +++ b/common/vlan.proto @@ -30,5 +30,5 @@ message Vlan { } extend Protocol { - optional Vlan vlan = 126; + optional Vlan vlan = 205; } diff --git a/common/vlanstack.proto b/common/vlanstack.proto index 203f3ef..d6bacd4 100644 --- a/common/vlanstack.proto +++ b/common/vlanstack.proto @@ -27,5 +27,5 @@ message VlanStack { } extend Protocol { - optional VlanStack vlanStack = 129; + optional VlanStack vlanStack = 208; } diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl From 8c0bfa84037ce5eaab1d59251423644087b36ce1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 15 Jul 2010 03:11:07 +0530 Subject: [PATCH 085/294] Short term fix for the local portgroup showing up as disconnected at startup --- client/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index ff947a9..dde1fd7 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -51,6 +51,8 @@ MainWindow::MainWindow(QWidget *parent) localServer_ = new QProcess(this); localServer_->setProcessChannelMode(QProcess::ForwardedChannels); localServer_->start(serverApp); + // TODO: waitForReadyRead() is a kludge till we implement auto-retry! + localServer_->waitForReadyRead(1000); pgl = new PortGroupList; From 716f5fb8f30f3bec7d0317799a0da93199e83f24 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 15 Jul 2010 07:01:43 +0530 Subject: [PATCH 086/294] Set the icon for the MainWindow --- client/mainwindow.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/mainwindow.ui b/client/mainwindow.ui index 45db008..333b2db 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -12,6 +12,9 @@ Ostinato + + :/icons/about.png + From 9b1dbfb1e63a18404e2e0a44b8dad9c079150614 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 16 Jul 2010 03:14:36 +0530 Subject: [PATCH 087/294] Mac OS X improvements - Application icon; 'make install' installs to /Applications; Ostinato is able to find drone at startup (as long as both bundles are present together) --- .hgignore | 3 +++ client/icons/logo.icns | Bin 0 -> 35391 bytes client/mainwindow.cpp | 5 +++++ client/ostinato.pro | 3 ++- install.pri | 7 ++++++- 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 client/icons/logo.icns diff --git a/.hgignore b/.hgignore index e27298d..e1d0ab6 100644 --- a/.hgignore +++ b/.hgignore @@ -4,6 +4,8 @@ syntax: glob *.o *.a *.exe +drone.app +ostinato.app # Qt generated files ui_*.h @@ -23,6 +25,7 @@ version.cpp # vim swap files *.swp +.DS_Store # ctags tags diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!T Date: Sat, 17 Jul 2010 03:54:35 +0530 Subject: [PATCH 088/294] Mac OS X improvements - Ostinato is now correctly capitalized in the MenuBar; default Wireshark path added for Mac OS X --- .hgignore | 3 +-- client/mainwindow.cpp | 2 +- client/ostinato.pro | 1 + client/settings.h | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.hgignore b/.hgignore index e1d0ab6..a88c51f 100644 --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,7 @@ syntax: glob *.o *.a *.exe -drone.app -ostinato.app +*.app # Qt generated files ui_*.h diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 6ecc7cd..87ae5d5 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -44,7 +44,7 @@ MainWindow::MainWindow(QWidget *parent) #ifdef Q_OS_MAC // applicationDirPath() does not return bundle, but executable inside bundle - serverApp.replace("ostinato.app", "drone.app"); + serverApp.replace("Ostinato.app", "drone.app"); #endif #ifdef Q_OS_WIN32 diff --git a/client/ostinato.pro b/client/ostinato.pro index 0b352c0..299ecc5 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,5 +1,6 @@ TEMPLATE = app CONFIG += qt +macx: TARGET = Ostinato win32:RC_FILE = ostinato.rc macx:ICON = icons/logo.icns QT += network script diff --git a/client/settings.h b/client/settings.h index fa22e54..3fbf4cf 100644 --- a/client/settings.h +++ b/client/settings.h @@ -26,9 +26,12 @@ along with this program. If not, see extern QSettings *appSettings; const QString kWiresharkPathKey("WiresharkPath"); -#ifdef Q_OS_WIN32 +#if defined(Q_OS_WIN32) const QString kWiresharkPathDefaultValue( "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); #else const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); #endif From 971713ae0f1d86f41c5fd1c8f7b51fbe969164b1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 21 Jul 2010 14:10:06 +0530 Subject: [PATCH 089/294] Replaced HTONX() and NTOHX() with qToBigEndian() and qFromBigEndian() --- rpc/pbrpcchannel.cpp | 26 ++++++++++++++------------ rpc/pbrpccommon.h | 39 --------------------------------------- rpc/rpcserver.cpp | 26 ++++++++++++++------------ 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index c89a2d6..47f1611 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -19,6 +19,8 @@ along with this program. If not, see #include "pbrpcchannel.h" +#include + PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) { isPending = false; @@ -130,9 +132,9 @@ void PbRpcChannel::CallMethod( Q_ASSERT(ret == true); len = req->ByteSize(); - *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_REQUEST); // type - *((quint16*)(msg+2)) = HTONS(method->index()); // method id - *((quint32*)(msg+4)) = HTONL(len); // len + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len // Avoid printing stats since it happens every couple of seconds if (pendingMethodId != 13) @@ -147,8 +149,8 @@ void PbRpcChannel::CallMethod( void PbRpcChannel::on_mpSocket_readyRead() { - char msg[MSGBUF_SIZE]; - char *p = (char*)&msg; + uchar msg[MSGBUF_SIZE]; + uchar *p = (uchar*) &msg; int msgLen; static bool parsing = false; static quint16 type, method; @@ -165,13 +167,13 @@ void PbRpcChannel::on_mpSocket_readyRead() return; } - msgLen = mpSocket->read(msg, PB_HDR_SIZE); + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); Q_ASSERT(msgLen == PB_HDR_SIZE); - type = NTOHS(GET16(p+0)); - method = NTOHS(GET16(p+2)); - len = NTOHL(GET32(p+4)); + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); //BUFDUMP(msg, PB_HDR_SIZE); //qDebug("type = %hu, method = %hu, len = %u", type, method, len); @@ -193,8 +195,8 @@ void PbRpcChannel::on_mpSocket_readyRead() { int l; - l = mpSocket->read(msg, sizeof(msg)); - blob->write(msg, l); + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); cumLen += l; } @@ -229,7 +231,7 @@ void PbRpcChannel::on_mpSocket_readyRead() return; } - msgLen = mpSocket->read(msg, sizeof(msg)); + msgLen = mpSocket->read((char*)msg, sizeof(msg)); Q_ASSERT((unsigned) msgLen == len); diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index 5cbbb57..0f4acf9 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -20,45 +20,6 @@ along with this program. If not, see #ifndef _PB_RPC_COMMON_H #define _PB_RPC_COMMON_H -//! \todo (LOW) check which one is right - wrong one seems to be working!!!!! -#if 0 -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+0) << 8 ) \ - | (*((quint8*)(p)+1))) -#else -#define GET16(p) (quint16)( \ - (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) -#define GET32(p) (quint32)( \ - (*((quint8*)(p)+3) << 24) \ - | (*((quint8*)(p)+2) << 16) \ - | (*((quint8*)(p)+1) << 8 ) \ - | (*((quint8*)(p)+0))) -#endif - -#define BYTESWAP4(x) \ - (((x & 0xFF000000) >> 24) | \ - ((x & 0x00FF0000) >> 8) | \ - ((x & 0x0000FF00) << 8) | \ - ((x & 0x000000FF) << 24)) - -#define BYTESWAP2(x) \ - (((x & 0xFF00) >> 8) | \ - ((x & 0x00FF) << 8)) - -//! \todo (LOW) : portability -#if 1 -#define HTONL(x) BYTESWAP4(x) -#define NTOHL(x) BYTESWAP4(x) -#define HTONS(x) BYTESWAP2(x) -#define NTOHS(x) BYTESWAP2(x) -#else -#define HTONL(x) (x) -#define NTOHL(x) (x) -#define HTONS(x) (x) -#define NTOHS(x) (x) -#endif - // Print a HexDump #define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ (len)).toHex()).toAscii().data()); diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 4b86b25..d48de21 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -20,6 +20,8 @@ along with this program. If not, see //#include "pbhelper.h" #include "rpcserver.h" +#include + RpcServer::RpcServer() { server = NULL; @@ -87,9 +89,9 @@ void RpcServer::done(PbRpcController *controller) len = blob->size(); qDebug("is binary blob of len %d", len); - *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_BINBLOB); // type - *((quint16*)(msg+2)) = HTONS(pendingMethodId); // method - (*(quint32*)(msg+4)) = HTONL(len); // len + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len clientSock->write(msg, PB_HDR_SIZE); @@ -118,9 +120,9 @@ void RpcServer::done(PbRpcController *controller) len = response->ByteSize(); - *((quint16*)(msg+0)) = HTONS(PB_MSG_TYPE_RESPONSE); // type - *((quint16*)(msg+2)) = HTONS(pendingMethodId); // method - *((quint32*)(msg+4)) = HTONL(len); // len + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 13) @@ -187,7 +189,7 @@ void RpcServer::when_error(QAbstractSocket::SocketError socketError) void RpcServer::when_dataAvail() { - char msg[MSGBUF_SIZE]; + uchar msg[MSGBUF_SIZE]; int msgLen; static bool parsing = false; static quint16 type, method; @@ -201,13 +203,13 @@ void RpcServer::when_dataAvail() if (clientSock->bytesAvailable() < PB_HDR_SIZE) return; - msgLen = clientSock->read(msg, PB_HDR_SIZE); + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); Q_ASSERT(msgLen == PB_HDR_SIZE); - type = NTOHS(GET16(&msg[0])); - method = NTOHS(GET16(&msg[2])); - len = NTOHL(GET32(&msg[4])); + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); //qDebug("type = %d, method = %d, len = %d", type, method, len); parsing = true; @@ -216,7 +218,7 @@ void RpcServer::when_dataAvail() if (clientSock->bytesAvailable() < len) return; - msgLen = clientSock->read(msg, sizeof(msg)); + msgLen = clientSock->read((char*)msg, sizeof(msg)); Q_ASSERT((unsigned) msgLen == len); if (type != PB_MSG_TYPE_REQUEST) From 3ed82b88ca1a007d37bea8f139093e3b7be1e9ec Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 26 Jul 2010 11:50:36 +0530 Subject: [PATCH 090/294] Updated .hgignore --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index a88c51f..cd5490f 100644 --- a/.hgignore +++ b/.hgignore @@ -5,6 +5,8 @@ syntax: glob *.a *.exe *.app +drone +ostinato # Qt generated files ui_*.h From 30bd0cd7c8dad5c603c32cc5ab0e64fa9147b07f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 4 Aug 2010 16:56:08 +0530 Subject: [PATCH 091/294] First cut code for IGMP/MLD --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 ++++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 63 ++ client/mainwindow.cpp | 118 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 ++++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 84 ++ client/ostinato.qrc | 37 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 262 +++++ client/port.h | 130 +++ client/portgroup.cpp | 782 ++++++++++++++ client/portgroup.h | 136 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 338 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 +++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 315 ++++++ client/portstatsmodel.h | 141 +++ client/portstatswindow.cpp | 180 ++++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 ++++ client/portswindow.cpp | 518 +++++++++ client/portswindow.h | 80 ++ client/portswindow.ui | 248 +++++ client/preferences.cpp | 56 + client/preferences.h | 41 + client/preferences.ui | 114 ++ client/settings.h | 41 + client/stream.cpp | 79 ++ client/stream.h | 42 + client/streamconfigdialog.cpp | 994 ++++++++++++++++++ client/streamconfigdialog.h | 135 +++ client/streamconfigdialog.ui | 1386 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 283 +++++ client/streammodel.h | 78 ++ common/abstractprotocol.cpp | 812 +++++++++++++++ common/abstractprotocol.h | 157 +++ common/arp.cpp | 953 +++++++++++++++++ common/arp.h | 120 +++ common/arp.proto | 67 ++ common/arp.ui | 518 +++++++++ common/comboprotocol.h | 217 ++++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot3.cpp | 168 +++ common/dot3.h | 75 ++ common/dot3.proto | 31 + common/dot3.ui | 59 ++ common/eth2.cpp | 172 +++ common/eth2.h | 75 ++ common/eth2.proto | 31 + common/eth2.ui | 66 ++ common/fileformat.cpp | 411 ++++++++ common/fileformat.h | 59 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 768 ++++++++++++++ common/gmp.h | 178 ++++ common/gmp.proto | 93 ++ common/gmp.ui | 675 ++++++++++++ common/icmp.cpp | 594 +++++++++++ common/icmp.h | 117 +++ common/icmp.proto | 44 + common/icmp.ui | 178 ++++ common/igmp.cpp | 370 +++++++ common/igmp.h | 62 ++ common/igmp.proto | 27 + common/intcombobox.h | 69 ++ common/ip4.cpp | 730 +++++++++++++ common/ip4.h | 114 ++ common/ip4.proto | 65 ++ common/ip4.ui | 469 +++++++++ common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip6.cpp | 940 +++++++++++++++++ common/ip6.h | 130 +++ common/ip6.proto | 61 ++ common/ip6.ui | 467 +++++++++ common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 195 ++++ common/llc.h | 80 ++ common/llc.proto | 32 + common/llc.ui | 108 ++ common/mac.cpp | 317 ++++++ common/mac.h | 90 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/mld.cpp | 341 ++++++ common/mld.h | 65 ++ common/mld.proto | 27 + common/ostproto.pro | 120 +++ common/payload.cpp | 258 +++++ common/payload.h | 84 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/protocol.proto | 234 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 196 ++++ common/protocolmanager.h | 55 + common/sample.cpp | 534 ++++++++++ common/sample.h | 102 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/snap.cpp | 193 ++++ common/snap.h | 77 ++ common/snap.proto | 31 + common/snap.ui | 63 ++ common/streambase.cpp | 473 +++++++++ common/streambase.h | 144 +++ common/svlan.cpp | 68 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/tcp.cpp | 699 +++++++++++++ common/tcp.h | 100 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/textproto.cpp | 261 +++++ common/textproto.h | 89 ++ common/textproto.proto | 37 + common/textproto.ui | 79 ++ common/udp.cpp | 492 +++++++++ common/udp.h | 87 ++ common/udp.proto | 39 + common/udp.ui | 174 ++++ common/userscript.cpp | 609 +++++++++++ common/userscript.h | 181 ++++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/vlan.cpp | 257 +++++ common/vlan.h | 82 ++ common/vlan.proto | 34 + common/vlan.ui | 179 ++++ common/vlanstack.h | 30 + common/vlanstack.proto | 31 + install.pri | 14 + ost.pro | 7 + protobuf.pri | 33 + rpc/pbhelper.h | 170 +++ rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 336 ++++++ rpc/pbrpcchannel.h | 102 ++ rpc/pbrpccommon.h | 41 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 280 +++++ rpc/rpcserver.h | 63 ++ server/abstractport.cpp | 250 +++++ server/abstractport.h | 104 ++ server/drone.cpp | 102 ++ server/drone.h | 56 + server/drone.pro | 44 + server/drone.qrc | 5 + server/drone.ui | 190 ++++ server/drone_main.cpp | 49 + server/icons/portgroup.png | Bin 0 -> 667 bytes server/myservice.cpp | 470 +++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 503 +++++++++ server/pcapport.h | 149 +++ server/portmanager.cpp | 77 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 215 ++++ server/winpcapport.h | 56 + version.pri | 19 + 230 files changed, 33713 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/comboprotocol.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/ostproto.pro create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone.qrc create mode 100644 server/drone.ui create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..34cd66c --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2010 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/protocolmanager.h" + +#include +#include +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; +QMainWindow *mainWindow; + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + delete mainWindow; + delete appSettings; + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..87ae5d5 --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,118 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp); + // TODO: waitForReadyRead() is a kludge till we implement auto-retry! + localServer_->waitForReadyRead(1000); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + statsDock = new QDockWidget(tr("Statistics"), this); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..299ecc5 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,84 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script +INCLUDEPATH += "../rpc/" "../common/" +LIBS += -lprotobuf +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..bd88fd1 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,37 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..6b5e13e --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,262 @@ +/* +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 +*/ + +#include "port.h" + +#include "fileformat.h" + +#include +#include +#include +#include + +uint Port::mAllocStreamId = 0; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +bool Port::newStreamAt(int index) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + OstProto::StreamConfigList streams; + + if (!fileFormat.openStreams(fileName, streams, error)) + goto _fail; + + if (!append) + { + while (numStreams()) + deleteStreamAt(0); + } + + for (int i = 0; i < streams.stream_size(); i++) + { + newStreamAt(mStreams.size()); + streamByIndex(mStreams.size()-1)->protoDataCopyFrom(streams.stream(i)); + } + + emit streamListChanged(mPortGroupId, mPortId); + + return true; + +_fail: + return false; +} + +bool Port::saveStreams(QString fileName, QString &error) +{ + OstProto::StreamConfigList streams; + + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + + return fileFormat.saveStreams(streams, fileName, error); +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..e4bb9c9 --- /dev/null +++ b/client/port.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString &error); + +signals: + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..faa1f0a --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,782 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, bool isExclusive) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + port->set_is_exclusive_control(isExclusive); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portGroupDataChanged(mPortGroupId); + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..dbf831c --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,136 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { rpcChannel->establish(ip, port); } + void disconnectFromHost() { rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, bool isExclusive); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..1d13eda --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..92e6a24 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,315 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..d50508b --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,141 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..58070a0 --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,518 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include +#include +#include + +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& /*previous*/) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + updatePortViewActions(tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + plm->portGroup(current.parent()).modifyPort(current.row(), checked); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + bool append = true; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams")); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?")); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + if (!plm->port(current).openStreams(fileName, append, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams")); + if (fileName.isEmpty()) + goto _exit; + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..ed93854 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a724c06 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,248 @@ + + PortsWindow + + + + 0 + 0 + 689 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Capacity + + + + + + + + + + Aggr fps + + + + + + + + + + % age + + + + + + + + + + Aggr bps + + + + + + + + + + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..4f372ee --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,56 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..1821346 --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..514ed42 --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,114 @@ + + Preferences + + + + 0 + 0 + 400 + 164 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + Wireshark Path + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..3fbf4cf --- /dev/null +++ b/client/settings.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..a24c971 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,79 @@ +/* +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 +*/ + +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..913e773 --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,994 @@ +/* +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 +*/ + +#include + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager *OstProtocolManager; + +int StreamConfigDialog::lastTopLevelTabIndex = 0; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setDisabled(true); + pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); + + // Finally, restore the saved last selected tab for the various tab widgets + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + //! \todo - doesn't seem to work - range validator needs a spinbox? + //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + QWidget *selWidget; + + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + selWidget = tbProtocolData->currentWidget(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + tbProtocolData->setCurrentWidget(selWidget); + + tbProtocolData->show(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if (!mpStream->preflightCheck(log)) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastTopLevelTabIndex = twTopLevel->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..5709570 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,135 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 1522 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time + static int lastTopLevelTabIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..8f4ed81 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1386 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 626 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + 0099; + + + + + + 4 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + 32767 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + 0099; + + + + + + 4 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 584 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto Stream + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + Qt::Horizontal + + + + 41 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + leNumPackets + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bursts/Sec + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Gaps + + + false + + + false + + + + + + + + + icons/gaps.png + + + + + + + ISG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leNumPackets + + + + + + + IBG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
dumpview.h
+ 1 +
+
+ + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 558 + 453 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 169 + 207 + + + 169 + 207 + + + + + rbSendPackets + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 125 + 207 + + + 156 + 207 + + + + +
diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..19942fd --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,283 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + return (int) StreamMaxFields; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..9c7e1aa --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..5462b19 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,812 @@ +/* +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 +*/ + +#include + +#include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..053e303 --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,157 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..844a2d7 --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,953 @@ +/* +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 +*/ + +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + return true; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..677b73a --- /dev/null +++ b/common/arp.h @@ -0,0 +1,120 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..29cf71d --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..e97bdc0 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,168 @@ +/* +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 +*/ + +#include +#include + +#include "dot3.h" +#include "streambase.h" + + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Dot3Protocol::~Dot3Protocol() +{ + delete configForm; +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len; + + len = protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len; + + len = protocolFramePayloadSize(streamIndex); + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len; + QByteArray fv; + + len = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +QWidget* Dot3Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); +} + +void Dot3Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_length(configForm->leLength->text().toULong(&isOk)); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..8a0d5c2 --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..2e4070c --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..7473ee2 --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,59 @@ + + dot3 + + + + 0 + 0 + 131 + 72 + + + + Form + + + + + + 802.3 + + + + + + Length + + + leLength + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..dd85063 --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,172 @@ +/* +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 +*/ + +#include +#include + +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Eth2Protocol::~Eth2Protocol() +{ + delete configForm; +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + quint16 type; + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + type = payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + } + default: + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..dcb8a7a --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_fieldCount + }; + +public: + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..72d58c8 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..7bf6cdf --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,66 @@ + + eth2 + + + + 0 + 0 + 267 + 64 + + + + Form + + + + + + Ethernet Type + + + leType + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..619ab37 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,411 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + Q_ASSERT(meta.data().format_version_minor() == kFileFormatVersionMinor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..d181567 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,59 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "fileformat.pb.h" + +#include +#include + +class FileFormat : public QObject +{ + Q_OBJECT +public: + FileFormat(); + ~FileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + +private: + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 1; + static const uint kFileFormatVersionRevision = 0; + + void initFileMetaData(OstProto::FileMetaData &metaData); +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..134b333 --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,768 @@ +/* +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 +*/ + +#include "gmp.h" + +#include + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); +} + +void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(0); + ssmWidget->show(); + break; + + case kIgmpV3Report: + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(1); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +GmpProtocol::~GmpProtocol() +{ + delete configForm; +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + switch(msgType()) + { + // IGMP + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + return FIELD_COUNT_ASM_ALL; + + case kIgmpV3Query: + return FIELD_COUNT_SSM_QUERY; + case kIgmpV3Report: + return FIELD_COUNT_SSM_REPORT; + + // MLD + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + return FIELD_COUNT_ASM_ALL; + + case kMldV2Query: + return FIELD_COUNT_SSM_QUERY; + case kMldV2Report: + return FIELD_COUNT_SSM_REPORT; + + default: + return FIELD_COUNT_ASM_ALL; + } +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(type); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case kMldMrt: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + int rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("Querier's Robustness Variable (QRV)"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("Querier's Robustness Variable (QRV)"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + quint8 qqic = qqi; // TODO: derive code from qqi + return QByteArray(1, char(qqic)); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + // XXX:Handled by each subclass + break; + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); // TODO: QQIC or QQI?? + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + // XXX: Handled by subclass + break; + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // TODO: check msg types and then return value appropriately + return true; +} + +QWidget* GmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new GmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void GmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); + configForm->maxResponseTime->setText(QString("%1").arg( + data.max_response_time())); + configForm->overrideChecksum->setChecked( + fieldData(kIsOverrideChecksum, FieldValue).toBool()); + configForm->checksum->setText(uintToHexStr( + fieldData(kChecksum, FieldValue).toUInt(), 2)); + + configForm->groupAddress->setText( + fieldData(kGroupAddress, FieldValue).toString()); + configForm->groupMode->setCurrentIndex( + fieldData(kGroupMode, FieldValue).toUInt()); + configForm->groupCount->setText( + fieldData(kGroupCount, FieldValue).toString()); + configForm->groupPrefix->setText( + fieldData(kGroupPrefix, FieldValue).toString()); + + configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); + configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); + configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + + configForm->overrideSourceCount->setChecked( + fieldData(kIsOverrideSourceCount, FieldValue).toBool()); + configForm->sourceCount->setText( + fieldData(kSourceCount, FieldValue).toString()); + // TODO: sources + + configForm->overrideGroupRecordCount->setChecked( + fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); + configForm->groupRecordCount->setText( + fieldData(kGroupRecordCount, FieldValue).toString()); + // TODO: groups +} + +void GmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(kType, configForm->msgTypeCombo->currentValue()); + setFieldData(kMldMrt, configForm->maxResponseTime->text()); + setFieldData(kIsOverrideChecksum, + configForm->overrideChecksum->isChecked()); + setFieldData(kChecksum, + configForm->checksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kGroupAddress, configForm->groupAddress->text()); + setFieldData(kGroupMode, configForm->groupMode->currentIndex()); + setFieldData(kGroupCount, configForm->groupCount->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text()); + + setFieldData(kSFlag, configForm->sFlag->isChecked()); + setFieldData(kQrv, configForm->qrv->text()); + setFieldData(kQqic, configForm->qqi->text()); + + setFieldData(kIsOverrideSourceCount, + configForm->overrideSourceCount->isChecked()); + setFieldData(kSourceCount, configForm->sourceCount->text()); + // TODO: Sources + + setFieldData(kIsOverrideGroupRecordCount, + configForm->overrideGroupRecordCount->isChecked()); + setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); + // TODO: Group Records +#if 0 + setFieldData(kA, configForm->gmpA->text()); + setFieldData(kB, configForm->gmpB->text()); + + setFieldData(kPayloadLength, configForm->gmpPayloadLength->text()); + setFieldData(kIs_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(kChecksum, configForm->gmpChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kX, configForm->gmpX->text()); + setFieldData(kY, configForm->gmpY->text()); +#endif +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..cc9cdf8 --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,178 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "gmp.pb.h" +#include "ui_gmp.h" + +#include "abstractprotocol.h" + + enum GmpMsgType + { + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, + + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F + }; + +/* +TODO:FIXME +Gmp Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class GmpConfigForm : public QWidget, public Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class GmpProtocol : public AbstractProtocol +{ +public: + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Used ONLY in - + // IGMPv1: Query, Report + // IGMPv2: Report, Leave (v2 uses v1 Query only) + // IGMPv3: Query + // MLDv1: Query, Report, Done + // MLDv2: Query + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Used ONLY in - + // IGMPv3: Query + // MLDv2: Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Used ONLY in - + // IGMPv3: Report + // MLDv2: Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + OstProto::Gmp data; + GmpConfigForm *configForm; + + GmpMsgType msgType() const + { + return GmpMsgType(fieldData(kType, FieldValue).toUInt()); + } + bool isSsmReport() const + { + return ((msgType() == kIgmpV3Report) + || (msgType() == kMldV2Report )); + } + bool isQuery() const + { + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query) + || (msgType() == kMldV1Query ) + || (msgType() == kMldV2Query )); + } + bool isSsmQuery() const + { + return ((msgType() == kIgmpV3Query) + || (msgType() == kMldV2Query )); + } + + virtual quint16 checksum(int streamIndex) const = 0; +}; + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..57e4eef --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,93 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // TODO: field numbers and default values + + optional uint32 type = 1; // TODO: default value + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3 [default = 0]; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4[default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // used by + // IGMPv1: Query, Report + // IGMPv2: Report, Leave (v2 uses v1 Query only) + // MLDv1: Query, Report, Done + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; // TODO: default value + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; // TODO: verify mcast ip range + + // used by + // IGMPv3: Query + // MLDv2: Query + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // used by + // IGMPv3: Report + // MLDv2: Report + message GroupRecord { + enum RecordType { + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; // TODO: default + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..9be18af --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,675 @@ + + Gmp + + + + 0 + 0 + 586 + 321 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag + + + + + + + QRV + + + qrv + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QQI + + + qqi + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 61 + 81 + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + Type + + + + + Group Address + + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + + + Record Type + + + recordType + + + + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + groupList + overrideGroupRecordCount + groupRecordCount + recordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + groupRecordSourceList + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + sourceList + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..e7f6267 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,594 @@ +/* +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 +*/ + + +#include "icmp.h" + +#include +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->versionGroup->button(icmpVersion())->click(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..a3fc296 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,117 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + QButtonGroup *versionGroup; + + IcmpConfigForm(QWidget *parent = 0); +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..ce7dd3e --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,370 @@ +/* +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 +*/ + +#include "igmp.h" + +#include +#include + + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ +} + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + quint8 mrt = 0, mrc; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrc = mrt; // TODO: MR Code + } + else if (msgType() == kIgmpV2Query) + mrc = mrt = data.max_response_time() & 0xFF; + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrc); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = data.group_address().v4(); // FIXME +#if 0 // TODO + getip( + data.group_address().v4(), + data.group_mode(), + data.group_count(), + data.group_prefix()); +#endif + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + return QVariant(); // FIXME + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i), (uchar*) (fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + return QVariant(); // FIXME + case FieldFrameValue: + { + QByteArray fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + + rv.resize(4 + 4 + 4*data.group_records(i).sources_size() + + data.group_records(i).aux_data().size()); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size(); + if (rec.is_override_source_count()) + qToBigEndian(rec.source_count(),(uchar*)(rv.data()+2)); + else + qToBigEndian(rec.sources_size(),(uchar*)(rv.data()+2)); + qToBigEndian(rec.group_address().v4(), + (uchar*)(fv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + qToBigEndian(rec.sources(j),(uchar*)(rv.data()+8+4*j)); + rv.append(QString().fromStdString(rec.aux_data()).toUtf8()); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append("Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size())); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + str.append(QString("; Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList l; + for (int j = 0; j < rec.sources_size(); j++) + l.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(l.join(", ")); + str.append(QString().fromStdString(rec.aux_data())); + + list.append(str); + } + return list.join("\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + //TODO + break; + + case kGroupRecords: + //TODO + break; + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +void IgmpProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kRsvdMrtCode, FieldValue).toString()); +#if 0 + configForm->igmpA->setText(fieldData(igmp_a, FieldValue).toString()); + configForm->igmpB->setText(fieldData(igmp_b, FieldValue).toString()); + + configForm->igmpPayloadLength->setText( + fieldData(igmp_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(igmp_is_override_checksum, FieldValue).toBool()); + configForm->igmpChecksum->setText(uintToHexStr( + fieldData(igmp_checksum, FieldValue).toUInt(), 2)); + + configForm->igmpX->setText(fieldData(igmp_x, FieldValue).toString()); + configForm->igmpY->setText(fieldData(igmp_y, FieldValue).toString()); +#endif +} + +void IgmpProtocol::storeConfigWidget() +{ + bool isOk; + + GmpProtocol::storeConfigWidget(); + +#if 0 + setFieldData(igmp_a, configForm->igmpA->text()); + setFieldData(igmp_b, configForm->igmpB->text()); + + setFieldData(igmp_payloadLength, configForm->igmpPayloadLength->text()); + setFieldData(igmp_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(igmp_checksum, configForm->igmpChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(igmp_x, configForm->igmpX->text()); + setFieldData(igmp_y, configForm->igmpY->text()); +#endif +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; +#if 0 // FIXME + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; +#endif + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..0905ac3 --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "igmp.pb.h" +#include "gmp.h" + +class IgmpConfigForm : public GmpConfigForm +{ +public: + IgmpConfigForm(QWidget *parent = 0); +private slots: +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual quint16 checksum(int streamIndex) const; +}; + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..776752f --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,730 @@ +/* +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 +*/ + +#include +#include + +#include "ip4.h" + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Ip4Protocol::~Ip4Protocol() +{ + delete configForm; +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip4Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); + + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + configWidget(); + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..ab7b3de --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,114 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..64407ad --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,65 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..c457a2d --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,469 @@ + + ip4 + + + + 0 + 0 + 493 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + Protocol + + + + + + + false + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 118 + 43 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 79 + 97 + + + 172 + 97 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..f8b7555 --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,940 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi, hostLo; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi, hostLo; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..1856bc8 --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..76e8e46 --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,195 @@ +/* +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 +*/ + +#include +#include + +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +LlcProtocol::~LlcProtocol() +{ + delete configForm; +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = (id >> 16) & 0xFF; + ssap = (id >> 8) & 0xFF; + ctl = (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* LlcProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configWidget(); + + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); + data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); + data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..2cc9fe8 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..e0681aa --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..5f305a0 --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,108 @@ + + llc + + + + 0 + 0 + 304 + 72 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + leDsap + + + + + + + false + + + >HH; + + + + + + + SSAP + + + leSsap + + + + + + + false + + + >HH; + + + + + + + Control + + + leControl + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..040f870 --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,317 @@ +/* +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 +*/ + +#include +#include + +#include "mac.h" + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +MacProtocol::~MacProtocol() +{ + delete configForm; +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + + +QWidget* MacProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..48d0e35 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,90 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..5d9151c --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,341 @@ +/* +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 +*/ + +#include "mld.h" + +#include +#include + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ +} + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrc; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrc = mrt; // TODO: MR Code + } + if (msgType() == kMldV1Query) + mrc = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrc, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi, grpLo; +#if 0 + AbstractProtocol::getip( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_mode(), + data.group_count(), + data.group_prefix(), + &grpHi, + &grpLo); +#endif + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + quint64 grpHi, grpLo; + + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + return QVariant(); // FIXME + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(grpHi, (uchar*)(fv.data() + i*16)); + qToBigEndian(grpLo, (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(grpHi, (uchar*)fv.data()); + qToBigEndian(grpLo, (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + // TODO + break; + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + //TODO + break; + + case kGroupRecords: + //TODO + break; + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +void MldProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kMldMrt, FieldValue).toString()); +#if 0 + configForm->mldA->setText(fieldData(mld_a, FieldValue).toString()); + configForm->mldB->setText(fieldData(mld_b, FieldValue).toString()); + + configForm->mldPayloadLength->setText( + fieldData(mld_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(mld_is_override_checksum, FieldValue).toBool()); + configForm->mldChecksum->setText(uintToHexStr( + fieldData(mld_checksum, FieldValue).toUInt(), 2)); + + configForm->mldX->setText(fieldData(mld_x, FieldValue).toString()); + configForm->mldY->setText(fieldData(mld_y, FieldValue).toString()); +#endif +} + +void MldProtocol::storeConfigWidget() +{ + bool isOk; + + GmpProtocol::storeConfigWidget(); + +#if 0 + setFieldData(mld_a, configForm->mldA->text()); + setFieldData(mld_b, configForm->mldB->text()); + + setFieldData(mld_payloadLength, configForm->mldPayloadLength->text()); + setFieldData(mld_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(mld_checksum, configForm->mldChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(mld_x, configForm->mldX->text()); + setFieldData(mld_y, configForm->mldY->text()); +#endif +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..77927fc --- /dev/null +++ b/common/mld.h @@ -0,0 +1,65 @@ +/* +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 +*/ + +#ifndef _MLD_H +#define _MLD_H + +#include "mld.pb.h" +#include "gmp.h" + +#include "abstractprotocol.h" + +class MldConfigForm : public GmpConfigForm +{ +public: + MldConfigForm(QWidget *parent = 0); +private slots: +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +protected: + virtual quint16 checksum(int streamIndex) const; +}; + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..3fdbc39 --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,120 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network script +LIBS += \ + -lprotobuf +FORMS += \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + sample.ui +PROTOS += \ + protocol.proto \ + fileformat.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + sample.proto +HEADERS += \ + abstractprotocol.h \ + comboprotocol.h \ + fileformat.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + arp.h \ + ip4.h \ + ip6.h \ + ip6over4.h \ + ip4over6.h \ + ip4over4.h \ + ip6over6.h \ + icmp.h \ + gmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + userscript.h \ + sample.h +SOURCES += \ + abstractprotocol.cpp \ + crc32c.cpp \ + fileformat.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + icmp.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + userscript.cpp \ + sample.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..d5fb2e5 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,258 @@ +/* +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 +*/ + +#include +#include + +//#include "../client/stream.h" +#include "payload.h" +#include "streambase.h" + + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +PayloadProtocol::~PayloadProtocol() +{ + delete configForm; +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + + +QWidget* PayloadProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void PayloadProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..2fd32d2 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,84 @@ +/* +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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..36caf12 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,234 @@ +/* +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 +*/ + +package OstProto; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..abcc518 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,196 @@ +/* +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 +*/ + +#include "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" +#include "vlan.h" +#include "vlanstack.h" +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" +#include "ip4over4.h" +#include "ip6over6.h" +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" +#include "textproto.h" +#include "userscript.h" +#include "sample.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + while (!protocolList.isEmpty()) + delete protocolList.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.append(p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..ac64919 --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,55 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QList protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..2a79660 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,534 @@ +/* +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 +*/ + +#include + +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +SampleProtocol::~SampleProtocol() +{ + delete configForm; +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +QWidget* SampleProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ +void SampleProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ +void SampleProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..91e6573 --- /dev/null +++ b/common/sample.h @@ -0,0 +1,102 @@ +/* +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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "sample.pb.h" +#include "ui_sample.h" + +#include "abstractprotocol.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + +public: + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..6803253 --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,193 @@ +/* +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 +*/ + +#include +#include + +#include "snap.h" + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +SnapProtocol::~SnapProtocol() +{ + delete configForm; +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + return data.oui(); + case FieldTextValue: + return QString("%1").arg(data.oui(), 6, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.oui(), (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* SnapProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); + data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); +} + diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..2e60b84 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,77 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, + + snap_fieldCount + }; + +public: + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..1354100 --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..80997bd --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,63 @@ + + snap + + + + 0 + 0 + 194 + 72 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + + diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..6ba1909 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,473 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + proto = OstProtocolManager->createProtocol( + stream.protocol(i).protocol_id().id(), this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +quint32 StreamBase::packetRate() const +{ + return (quint32) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(quint32 packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +quint32 StreamBase::burstRate() const +{ + return (quint32) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(quint32 burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + goto _fail; + } + } + return true; + +_fail: + return false; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..7afbbd9 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,144 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + quint32 packetRate() const; + bool setPacketRate(quint32 packetsPerSec); + + quint32 burstRate() const; + bool setBurstRate(quint32 burstsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..893671d --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,68 @@ +/* +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 +*/ + +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..02faa6a --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,699 @@ +/* +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 +*/ + +#include +#include + +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +TcpProtocol::~TcpProtocol() +{ + delete configForm; +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* TcpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); + + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); + + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); + + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); + + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); + + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + int ff = 0; + + configWidget(); + + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); + + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); + + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); + + setFieldData(tcp_window, configForm->leTcpWindow->text()); + + setFieldData(tcp_cksum, configForm->leTcpCksum->text()); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); + + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + setFieldData(tcp_flags, ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..265937a --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..c86f7ff --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,261 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + return QString().fromStdString(data.text()).toUtf8(); + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..57b71cc --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text encoded with the specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..12e5b4e --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..b3cf278 --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,79 @@ + + TextProtocol + + + + 0 + 0 + 493 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + +
diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..8f3c35b --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,492 @@ +/* +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 +*/ + +#include +#include + +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +UdpProtocol::~UdpProtocol() +{ + delete configForm; +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* UdpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); + + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); + + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); + + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); + + setFieldData(udp_cksum, configForm->leUdpCksum->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..623e999 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,87 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..fa084f1 --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,609 @@ +/* +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 +*/ + +#include "userscript.h" + +#include + +// +// -------------------- UserScriptConfigForm -------------------- +// + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), protocol_(protocol) +{ + setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } + updateStatus(); +} + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + configForm = NULL; + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..4ef0d91 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,181 @@ +/* +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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; +}; + + + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + void updateStatus(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + + +class UserScriptProtocol : public AbstractProtocol +{ + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..95b2304 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,257 @@ +/* +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 +*/ + +#include + +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +VlanProtocol::~VlanProtocol() +{ + delete configForm; +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..728572b --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +protected: + OstProto::Vlan data; + +public: + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..3e0326d --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,179 @@ + + Vlan + + + + 0 + 0 + 268 + 96 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..bfe4e1a --- /dev/null +++ b/ost.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..27ec61b --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..47f1611 --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,336 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[MSGBUF_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + ret = req->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); + Q_ASSERT(ret == true); + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE + len); + } + + mpSocket->write(msg, PB_HDR_SIZE + len); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[MSGBUF_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + // Wait till we have the entire message + if (mpSocket->bytesAvailable() < len) + { + qDebug("client: not enough data available for a complete msg"); + return; + } + + msgLen = mpSocket->read((char*)msg, sizeof(msg)); + + Q_ASSERT((unsigned) msgLen == len); + + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + response->ParseFromArray((void*) msg, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..682c553 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,102 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..0f4acf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#define MSGBUF_SIZE 4096 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..d48de21 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,280 @@ +/* +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 +*/ + +//#include "pbhelper.h" +#include "rpcserver.h" + +#include + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[MSGBUF_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcServer::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + response->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE + len); + +_exit: + delete controller; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcServer::when_dataAvail() +{ + uchar msg[MSGBUF_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (clientSock->bytesAvailable() < len) + return; + + msgLen = clientSock->read((char*)msg, sizeof(msg)); + Q_ASSERT((unsigned) msgLen == len); + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + req->ParseFromArray((void*)msg, len); + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); + + parsing = false; + + return; + +_error_exit: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..1e9c93e --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,63 @@ +/* +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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..954278b --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,250 @@ +/* +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 +*/ + +#include "abstractport.h" + +#include +#include + +#include "../common/streambase.h" + +AbstractPort::AbstractPort(int id, const char *device) +{ + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = true; + linkState_ = OstProto::LinkStateUnknown; + + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::updatePacketList() +{ + int len; + bool isVariable; + uchar pktBuf[2000]; + long sec = 0; + long usec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + long numPackets, numBursts; + long ibg, ipg; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->numBursts(); + numPackets = streamList_[i]->burstSize(); + ibg = 1000000/streamList_[i]->burstRate(); + ipg = 0; + break; + case OstProto::StreamControl::e_su_packets: + numBursts = 1; + numPackets = streamList_[i]->numPackets(); + ibg = 0; + ipg = 1000000/streamList_[i]->packetRate(); + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %ld, numPackets = %ld\n", + numBursts, numPackets); + qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + + if (streamList_[i]->isFrameVariable()) + { + isVariable = true; + len = 0; // avoid compiler warning; get len value for each pkt + } + else + { + isVariable = false; + len = streamList_[i]->frameValue(pktBuf, sizeof(pktBuf), 0); + } + + for (int j = 0; j < numBursts; j++) + { + for (int k = 0; k < numPackets; k++) + { + if (isVariable) + { + len = streamList_[i]->frameValue(pktBuf, + sizeof(pktBuf), j * numPackets + k); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, sec, usec); + + appendToPacketList(sec, usec, pktBuf, len); + + usec += ipg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numPackets) + + usec += ibg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numBursts) + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg : ipg); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; + stats->rxBytes = stats_.rxBytes - epochStats_.rxBytes; + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = stats_.txPkts - epochStats_.txPkts; + stats->txBytes = stats_.txBytes - epochStats_.txBytes; + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..4b036fa --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,104 @@ +/* +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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + virtual void init(); + + int id() { return data_.port_id().id(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, long usecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + OstProto::Port data_; + OstProto::LinkState linkState_; + + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..8206ac2 --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,102 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + trayIcon_->hide(); + + delete trayIcon_; + delete trayIconMenu_; + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..7466a76 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include "ui_drone.h" + +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone +{ + Q_OBJECT + +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..b2b25d1 --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,44 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +RESOURCES += drone.qrc +HEADERS += drone.h +FORMS += drone.ui +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone.qrc b/server/drone.qrc new file mode 100644 index 0000000..a642656 --- /dev/null +++ b/server/drone.qrc @@ -0,0 +1,5 @@ + + + icons/portgroup.png + + diff --git a/server/drone.ui b/server/drone.ui new file mode 100644 index 0000000..e2e0613 --- /dev/null +++ b/server/drone.ui @@ -0,0 +1,190 @@ + + Drone + + + + 0 + 0 + 268 + 216 + + + + Drone + + + :/icons/portgroup.png + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + (Server) + + + Qt::AlignCenter + + + + + + + TODO: Info/Status here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Exit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show + + + + + Exit + + + + + + + + + pushButton + clicked() + actionExit + trigger() + + + 134 + 194 + + + -1 + -1 + + + + + actionShow + triggered() + Drone + showNormal() + + + -1 + -1 + + + 133 + 107 + + + + + actionExit + triggered() + Drone + close() + + + -1 + -1 + + + 133 + 107 + + + + + diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..3f16bcc --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,49 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +int myport; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Drone drone; + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone.objectName()); + + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone.init()) + exit(-1); + + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone.showMinimized(); + app.exec(); + return 0; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..222dd42 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,503 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + delete capturer_; + delete transmitter_; + delete monitorTx_; + delete monitorRx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
"); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
"); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "

%1
" + "Rx/Tx Rates are also subject to above limitation(s)

"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE]; + + direction_ = direction; + isDirectional_ = true; + stats_ = stats; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); +} + +void PcapPort::PortMonitor::run() +{ + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for sendQueueList + while(sendQueueList_.size()) + { + pcap_send_queue *sq = sendQueueList_.takeFirst(); + pcap_sendqueue_destroy(sq); + } + setPacketListLoopMode(false, 0); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + pcap_send_queue *sendQ; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = usec; + + sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); + + if ((sendQ == NULL) || + (sendQ->len + sizeof(pcap_pkthdr) + length) > sendQ->maxlen) + { + //! \todo (LOW): calculate sendqueue size + sendQ = pcap_sendqueue_alloc(1*1024*1024); + sendQueueList_.append(sendQ); + + // Validate that the pkt will fit inside the new sendQ + Q_ASSERT((length + sizeof(pcap_pkthdr)) < sendQ->maxlen); + } + + if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) + op = false; + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + + qDebug("sendQueueList_.size = %d", sendQueueList_.size()); + + for(i = 0; i < sendQueueList_.size(); i++) + { + int ret; +_restart: + ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); + + if (ret < 0) + { + qDebug("error %d in sendQueueTransmit()", ret); + stop_ = false; + return; + } + } + + if (returnToQIdx_ >= 0) + { + i = returnToQIdx_; + + udelay(loopDelay_); + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, int sync) +{ + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + while (1) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (stop_) + { + return -2; + } + + // A pktLen of size 0 is used at the end of a sendQueue and before + // the next sendQueue - i.e. for inter sendQueue timing + if(pktLen > 0) + { + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + } + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + // Check if the end of the user buffer has been reached + if((char*) hdr >= end) + return 0; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + if (usec) + { + udelay(usec); + ts = hdr->ts; + } + } + } +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#ifdef Q_OS_WIN32 + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } + + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); + if (handle_ == NULL) + { + qDebug("Error opening port %s: %s\n", + device_.toAscii().constData(), pcap_geterr(handle_)); + return; + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..a6264c5 --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,149 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0); + } + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, usec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, long usecDelay) { + transmitter_->setPacketListLoopMode(loop, usecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + protected: + AbstractPort::PortStats *stats_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, long usecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = usecDelay; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + + quint64 ticksFreq_; + QList sendQueueList_; + int returnToQIdx_; + long loopDelay_; + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..b4f7572 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,77 @@ +/* +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 +*/ + +#include "portmanager.h" + +#include +#include + +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + +#ifdef Q_OS_WIN32 + port = new WinPcapPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + port->init(); + portList_.append(port); + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + } + + pcap_freealldevs(deviceList); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..3f38a15 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,215 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..236cdfa --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.1.1 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From 82bcc756f0890888fc470e0f75e50d234a5b4af5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 4 Aug 2010 21:47:11 +0530 Subject: [PATCH 092/294] added prefix inputMask (not working); added some code for sourceList (not complete) --- common/gmp.cpp | 18 ++++++ common/gmp.h | 2 + common/gmp.ui | 166 ++++++++++++++++++++++++++++++------------------- 3 files changed, 122 insertions(+), 64 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index 134b333..63f1288 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -36,6 +36,24 @@ GmpConfigForm::GmpConfigForm(QWidget *parent) msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); } +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem("0.0.0.0"); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) { switch(msgTypeCombo->currentValue()) diff --git a/common/gmp.h b/common/gmp.h index cc9cdf8..2cbbd67 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -62,6 +62,8 @@ public: GmpConfigForm(QWidget *parent = 0); private slots: void on_msgTypeCombo_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); }; class GmpProtocol : public AbstractProtocol diff --git a/common/gmp.ui b/common/gmp.ui index 9be18af..73b4300 100755 --- a/common/gmp.ui +++ b/common/gmp.ui @@ -6,7 +6,7 @@ 0 0 586 - 321 + 281 @@ -171,6 +171,9 @@ 0 + + /900; + @@ -179,7 +182,7 @@ - 1 + 0 @@ -200,13 +203,13 @@ - - + + - + - S Flag + S Flag (Suppress Router Processing) @@ -221,6 +224,9 @@ + + + Qt::Horizontal @@ -233,9 +239,6 @@ - - - @@ -246,73 +249,109 @@ - + - - - - - - - - Source List - - - groupRecordAddress - - - - + - Qt::Horizontal + Qt::Vertical - 40 - 20 + 61 + 41 - - - - Count - - - - - - - false - - - - 0 - 0 - - - - - - - - - - - Qt::Vertical - - - - 61 - 81 - - - + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + A + + + + + + + D + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + @@ -587,7 +626,6 @@ qqi overrideSourceCount sourceCount - sourceList From be77148b117f74f5d20fbe05b8aa973ccd465b1d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 11 Aug 2010 18:47:25 +0530 Subject: [PATCH 093/294] Bumped version to 0.2 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index 236cdfa..c370b32 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.1.1 +APP_VERSION = 0.2 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 19eb4a171336e1321c6270074d36e5449f377e57 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Aug 2010 14:50:58 +0530 Subject: [PATCH 094/294] snapshot before rewriting SSM report code --- common/gmp.cpp | 207 ++++++++++++++++++---- common/gmp.h | 12 +- common/gmp.ui | 455 +++++++++++++++++++++++++++++------------------- common/igmp.cpp | 61 ++++++- common/igmp.h | 1 + common/mld.cpp | 39 +++++ common/mld.h | 1 + 7 files changed, 558 insertions(+), 218 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index 63f1288..51ae7fa 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -19,6 +19,7 @@ along with this program. If not, see #include "gmp.h" +#include #include GmpConfigForm::GmpConfigForm(QWidget *parent) @@ -34,24 +35,26 @@ GmpConfigForm::GmpConfigForm(QWidget *parent) msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); } -void GmpConfigForm::on_addSource_clicked() +GmpConfigForm::~GmpConfigForm() { - QListWidgetItem *item=new QListWidgetItem("0.0.0.0"); - item->setFlags(item->flags() | Qt::ItemIsEditable); - sourceList->insertItem(sourceList->currentRow(), item); + // delete UserRole itemdata for grpRecords + for (int i = 0; i < groupList->count(); i++) + { + QListWidgetItem *item = groupList->item(i); - if (!overrideSourceCount->isChecked()) - sourceCount->setText(QString().setNum(sourceList->count())); -} - -void GmpConfigForm::on_deleteSource_clicked() -{ - delete sourceList->takeItem(sourceList->currentRow()); - - if (!overrideSourceCount->isChecked()) - sourceCount->setText(QString().setNum(sourceList->count())); + if (item) + { + OstProto::Gmp::GroupRecord *rec = (OstProto::Gmp::GroupRecord*) + item->data(Qt::UserRole).value(); + item->setData(Qt::UserRole, QVariant()); + delete rec; + } + } } void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) @@ -91,6 +94,135 @@ void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) } } +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord *record = new OstProto::Gmp::GroupRecord; + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, QVariant::fromValue((void*)record)); + item->setText("xxx"); // FIXME + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + QListWidgetItem *item = groupList->takeItem(groupList->currentRow()); + if (item) + { + delete (OstProto::Gmp::GroupRecord*)item->data(Qt::UserRole) + .value(); + delete item; + } + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + OstProto::Gmp::GroupRecord *prevRec; + OstProto::Gmp::GroupRecord *currRec; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + prevRec = (OstProto::Gmp::GroupRecord*)previous->data(Qt::UserRole) + .value(); + + prevRec->set_type(OstProto::Gmp::GroupRecord::RecordType( + groupRecordType->currentIndex()+1)); + // FIXME: groupRecordAddress, sources + + prevRec->set_is_override_source_count( + overrideGroupRecordSourceCount->isChecked()); + prevRec->set_source_count(groupRecordSourceCount->text().toUInt()); + + prevRec->set_is_override_aux_data_length( + overrideAuxDataLength->isChecked()); + prevRec->set_aux_data(QString(QByteArray::fromHex( + QByteArray().append(auxData->text()))).toStdString()); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + currRec = (OstProto::Gmp::GroupRecord*)current->data(Qt::UserRole) + .value(); + + groupRecordType->setCurrentIndex(int(currRec->type()) - 1); + // FIXME: groupRecordAddress, sources + + overrideGroupRecordSourceCount->setChecked( + currRec->is_override_source_count()); + if (overrideGroupRecordSourceCount->isChecked()) + { + groupRecordSourceCount->setText( + QString().setNum(currRec->source_count())); + } + + overrideAuxDataLength->setChecked(currRec->is_override_aux_data_length()); + if (overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum(currRec->aux_data_length())); + auxData->setText(QString(QByteArray().append( + QString().fromStdString(currRec->aux_data())).toHex())); +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum(auxData->text().length()/2)); +} + GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { @@ -115,6 +247,16 @@ int GmpProtocol::fieldCount() const int GmpProtocol::frameFieldCount() const { + int count = 0; + + // TODO: optimize!!!!! + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + return count; +#if 0 switch(msgType()) { // IGMP @@ -144,6 +286,7 @@ int GmpProtocol::frameFieldCount() const default: return FIELD_COUNT_ASM_ALL; } +#endif } AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const @@ -225,7 +368,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return type; case FieldTextValue: - return QString("%1").arg(type); + return QString("%1").arg(quint8(type)); case FieldFrameValue: return QByteArray(1, quint8(type)); default: @@ -335,7 +478,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("Querier's Robustness Variable (QRV)"); + return QString("QRV"); case FieldValue: return qrv; case FieldTextValue: @@ -356,7 +499,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, switch(attrib) { case FieldName: - return QString("Querier's Robustness Variable (QRV)"); + return QString("QQIC"); case FieldValue: return qqi; case FieldTextValue: @@ -690,18 +833,6 @@ bool GmpProtocol::isProtocolFrameValueVariable() const return true; } -QWidget* GmpProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new GmpConfigForm; - loadConfigWidget(); - } - - return configForm; -} - void GmpProtocol::loadConfigWidget() { configWidget(); @@ -727,11 +858,14 @@ void GmpProtocol::loadConfigWidget() configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + configForm->sourceList->clear(); + configForm->sourceList->addItems( + fieldData(kSources, FieldValue).toStringList()); + configForm->overrideSourceCount->setChecked( fieldData(kIsOverrideSourceCount, FieldValue).toBool()); configForm->sourceCount->setText( fieldData(kSourceCount, FieldValue).toString()); - // TODO: sources configForm->overrideGroupRecordCount->setChecked( fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); @@ -756,21 +890,28 @@ void GmpProtocol::storeConfigWidget() setFieldData(kGroupAddress, configForm->groupAddress->text()); setFieldData(kGroupMode, configForm->groupMode->currentIndex()); setFieldData(kGroupCount, configForm->groupCount->text()); - setFieldData(kGroupPrefix, configForm->groupPrefix->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); setFieldData(kSFlag, configForm->sFlag->isChecked()); setFieldData(kQrv, configForm->qrv->text()); setFieldData(kQqic, configForm->qqi->text()); + QStringList list; + for (int i = 0; i < configForm->sourceList->count(); i++) + list.append(configForm->sourceList->item(i)->text()); + setFieldData(kSources, list); + + // XXX: sourceCount should be AFTER sources setFieldData(kIsOverrideSourceCount, configForm->overrideSourceCount->isChecked()); setFieldData(kSourceCount, configForm->sourceCount->text()); - // TODO: Sources + // TODO: Group Records + + // XXX: groupRecordCount should be AFTER groupRecords setFieldData(kIsOverrideGroupRecordCount, configForm->overrideGroupRecordCount->isChecked()); setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); - // TODO: Group Records #if 0 setFieldData(kA, configForm->gmpA->text()); setFieldData(kB, configForm->gmpB->text()); diff --git a/common/gmp.h b/common/gmp.h index 2cbbd67..c382005 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -60,10 +60,21 @@ class GmpConfigForm : public QWidget, public Ui::Gmp Q_OBJECT public: GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); +protected: + QString _defaultSourceIp; private slots: void on_msgTypeCombo_currentIndexChanged(int index); void on_addSource_clicked(); void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); }; class GmpProtocol : public AbstractProtocol @@ -87,7 +98,6 @@ public: virtual bool isProtocolFrameValueVariable() const; - virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/gmp.ui b/common/gmp.ui index 73b4300..6e1f0d8 100755 --- a/common/gmp.ui +++ b/common/gmp.ui @@ -5,8 +5,8 @@ 0 0 - 586 - 281 + 497 + 355 @@ -182,7 +182,7 @@ - 0 + 1 @@ -297,14 +297,14 @@ - A + + - D + -- @@ -351,6 +351,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -359,7 +372,7 @@ - + 0 @@ -381,18 +394,48 @@ - - - - Type - - - - - Group Address - - - + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + -- + + + + + + + @@ -415,162 +458,218 @@ - - - - - Record Type - - - recordType - - - - - - - - Is Include - - - - - Is Exclude - - - - - To Include - - - - - To Exclude - - - - - Allow New - - - - - Block Old - - - - - - - - Group Address - - - groupRecordAddress - - - - - - - - - - - - Source List - - - groupRecordAddress - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Count - - - - - - - false - - - - 0 - 0 - - - - - - - - - - - - - - - Aux Data - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Length - - - - - - - false - - - - 0 - 0 - - - - - - - - - - + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + -- + + + + + + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + @@ -586,8 +685,8 @@ - 20 - 40 + 101 + 21 @@ -610,14 +709,12 @@ groupMode groupCount groupPrefix - groupList overrideGroupRecordCount groupRecordCount - recordType + groupRecordType groupRecordAddress overrideGroupRecordSourceCount groupRecordSourceCount - groupRecordSourceList overrideAuxDataLength auxDataLength auxData diff --git a/common/igmp.cpp b/common/igmp.cpp index ce7dd3e..f7c0329 100644 --- a/common/igmp.cpp +++ b/common/igmp.cpp @@ -20,12 +20,36 @@ along with this program. If not, see #include "igmp.h" #include +#include #include +class IpAddressDelegate : public QItemDelegate +{ +public: + IpAddressDelegate(QObject *parent = 0) + : QItemDelegate(parent) { } + ~IpAddressDelegate() {} + QWidget* createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; + } +}; IgmpConfigForm::IgmpConfigForm(QWidget *parent) : GmpConfigForm(parent) { + _defaultSourceIp = "0.0.0.0"; + + sourceList->setItemDelegate(new IpAddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); } IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) @@ -90,7 +114,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, { case kRsvdMrtCode: { - quint8 mrt = 0, mrc; + quint8 mrt = 0, mrc = 0; if (msgType() == kIgmpV3Query) { @@ -111,7 +135,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return mrt; case FieldTextValue: - return QString("%1 ms").arg(mrt); + return QString("%1").arg(mrt); case FieldFrameValue: return QByteArray(1, mrc); default: @@ -156,13 +180,19 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("Source List"); case FieldValue: - return QVariant(); // FIXME + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } case FieldFrameValue: { QByteArray fv; fv.resize(4 * data.sources_size()); for (int i = 0; i < data.sources_size(); i++) - qToBigEndian(data.sources(i), (uchar*) (fv.data()+4*i)); + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); return fv; } case FieldTextValue: @@ -292,8 +322,17 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value, break; } case kSources: - //TODO + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } break; + } case kGroupRecords: //TODO @@ -308,6 +347,18 @@ _exit: return isOk; } +QWidget* IgmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new IgmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + void IgmpProtocol::loadConfigWidget() { GmpProtocol::loadConfigWidget(); diff --git a/common/igmp.h b/common/igmp.h index 0905ac3..c528fc8 100644 --- a/common/igmp.h +++ b/common/igmp.h @@ -52,6 +52,7 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/mld.cpp b/common/mld.cpp index 5d9151c..2f50535 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -19,12 +19,39 @@ along with this program. If not, see #include "mld.h" +#include "ipv6addressvalidator.h" + #include +#include #include +class IpAddressDelegate : public QItemDelegate +{ +public: + IpAddressDelegate(QObject *parent = 0) + : QItemDelegate(parent) { } + ~IpAddressDelegate() {} + QWidget* createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + // FIXME: const problem!!! + //ipEdit->setValidator(new IPv6AddressValidator(this)); + + return ipEdit; + } +}; + MldConfigForm::MldConfigForm(QWidget *parent) : GmpConfigForm(parent) { + _defaultSourceIp = "::"; + sourceList->setItemDelegate(new IpAddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); } MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) @@ -292,6 +319,18 @@ _exit: return isOk; } +QWidget* MldProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new MldConfigForm; + loadConfigWidget(); + } + + return configForm; +} + void MldProtocol::loadConfigWidget() { GmpProtocol::loadConfigWidget(); diff --git a/common/mld.h b/common/mld.h index 77927fc..0d087c9 100644 --- a/common/mld.h +++ b/common/mld.h @@ -56,6 +56,7 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); + virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); protected: From d4408f0fc1698c3e6cea212786d47a663a49c730 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 20 Aug 2010 07:22:21 +0530 Subject: [PATCH 095/294] more changes --- common/gmp.cpp | 322 ++++++++++++++++++++++++++++++++++++------------ common/gmp.h | 2 + common/gmp.ui | 20 ++- common/igmp.cpp | 107 +++++++++------- 4 files changed, 322 insertions(+), 129 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index 51ae7fa..559fa74 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -42,19 +42,13 @@ GmpConfigForm::GmpConfigForm(QWidget *parent) GmpConfigForm::~GmpConfigForm() { - // delete UserRole itemdata for grpRecords - for (int i = 0; i < groupList->count(); i++) - { - QListWidgetItem *item = groupList->item(i); +} - if (item) - { - OstProto::Gmp::GroupRecord *rec = (OstProto::Gmp::GroupRecord*) - item->data(Qt::UserRole).value(); - item->setData(Qt::UserRole, QVariant()); - delete rec; - } - } +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); } void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) @@ -114,11 +108,23 @@ void GmpConfigForm::on_deleteSource_clicked() void GmpConfigForm::on_addGroupRecord_clicked() { - OstProto::Gmp::GroupRecord *record = new OstProto::Gmp::GroupRecord; + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; QListWidgetItem *item = new QListWidgetItem; - item->setData(Qt::UserRole, QVariant::fromValue((void*)record)); - item->setText("xxx"); // FIXME + grpRec["groupRecordType"] = defRec.type()-1; + grpRec["groupRecordAddress"] = _defaultSourceIp; + grpRec["overrideGroupRecordSourceCount"] = defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QString().fromStdString(defRec.aux_data()); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); groupList->insertItem(groupList->currentRow(), item); @@ -128,13 +134,7 @@ void GmpConfigForm::on_addGroupRecord_clicked() void GmpConfigForm::on_deleteGroupRecord_clicked() { - QListWidgetItem *item = groupList->takeItem(groupList->currentRow()); - if (item) - { - delete (OstProto::Gmp::GroupRecord*)item->data(Qt::UserRole) - .value(); - delete item; - } + delete groupList->takeItem(groupList->currentRow()); if (!overrideGroupRecordCount->isChecked()) groupRecordCount->setText(QString().setNum(groupList->count())); @@ -143,8 +143,8 @@ void GmpConfigForm::on_deleteGroupRecord_clicked() void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { - OstProto::Gmp::GroupRecord *prevRec; - OstProto::Gmp::GroupRecord *currRec; + QVariantMap rec; + QStringList strList; qDebug("in %s", __FUNCTION__); @@ -152,46 +152,52 @@ void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, if (previous == NULL) goto _load_current_record; - prevRec = (OstProto::Gmp::GroupRecord*)previous->data(Qt::UserRole) - .value(); + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = auxData->text(); - prevRec->set_type(OstProto::Gmp::GroupRecord::RecordType( - groupRecordType->currentIndex()+1)); - // FIXME: groupRecordAddress, sources - - prevRec->set_is_override_source_count( - overrideGroupRecordSourceCount->isChecked()); - prevRec->set_source_count(groupRecordSourceCount->text().toUInt()); - - prevRec->set_is_override_aux_data_length( - overrideAuxDataLength->isChecked()); - prevRec->set_aux_data(QString(QByteArray::fromHex( - QByteArray().append(auxData->text()))).toStdString()); + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); _load_current_record: // ... and load current record if (current == NULL) goto _exit; - currRec = (OstProto::Gmp::GroupRecord*)current->data(Qt::UserRole) - .value(); + rec = current->data(Qt::UserRole).toMap(); - groupRecordType->setCurrentIndex(int(currRec->type()) - 1); - // FIXME: groupRecordAddress, sources - - overrideGroupRecordSourceCount->setChecked( - currRec->is_override_source_count()); - if (overrideGroupRecordSourceCount->isChecked()) + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) { - groupRecordSourceCount->setText( - QString().setNum(currRec->source_count())); + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(rec["auxData"].toString()); - overrideAuxDataLength->setChecked(currRec->is_override_aux_data_length()); - if (overrideAuxDataLength->isChecked()) - auxDataLength->setText(QString().setNum(currRec->aux_data_length())); - auxData->setText(QString(QByteArray().append( - QString().fromStdString(currRec->aux_data())).toHex())); _exit: groupRecord->setEnabled(current != NULL); return; @@ -219,8 +225,9 @@ void GmpConfigForm::on_deleteGroupRecordSource_clicked() void GmpConfigForm::on_auxData_textChanged(const QString &text) { + // auxDataLength is in units of words and each byte is 2 chars in text() if (!overrideAuxDataLength->isChecked()) - auxDataLength->setText(QString().setNum(auxData->text().length()/2)); + auxDataLength->setText(QString().setNum((auxData->text().size()+7)/8)); } GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) @@ -596,8 +603,120 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, break; } case kGroupRecords: - // XXX:Handled by each subclass + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type()-1; + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())).toHex(); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + if (rec.is_override_source_count()) + { + qToBigEndian(quint16(rec.source_count()), + (uchar*)(rv.data()+2)); + } + else + { + qToBigEndian(quint16(rec.sources_size()), + (uchar*)(rv.data()+2)); + } + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data()).toUtf8()); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } break; + } // Meta Fields case kIsOverrideChecksum: @@ -750,8 +869,36 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, break; } case kGroupRecords: - // XXX: Handled by subclass + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt() + 1)); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = QByteArray::fromHex( + grpRec["auxData"].toByteArray()); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + break; + } // Meta Fields case kIsOverrideChecksum: @@ -821,16 +968,18 @@ int GmpProtocol::protocolFrameSize(int streamIndex) const return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); } -/*! - TODO: If your protocol has any variable fields, return true \n - - Otherwise you don't need to reimplement this method - the base class always - returns false -*/ bool GmpProtocol::isProtocolFrameValueVariable() const { - // TODO: check msg types and then return value appropriately - return true; + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; } void GmpProtocol::loadConfigWidget() @@ -862,16 +1011,33 @@ void GmpProtocol::loadConfigWidget() configForm->sourceList->addItems( fieldData(kSources, FieldValue).toStringList()); + // NOTE: SourceCount should be loaded after sourceList configForm->overrideSourceCount->setChecked( fieldData(kIsOverrideSourceCount, FieldValue).toBool()); configForm->sourceCount->setText( fieldData(kSourceCount, FieldValue).toString()); + QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); + configForm->groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(configForm->groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + configForm->groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList configForm->overrideGroupRecordCount->setChecked( fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); configForm->groupRecordCount->setText( fieldData(kGroupRecordCount, FieldValue).toString()); - // TODO: groups + } void GmpProtocol::storeConfigWidget() @@ -880,6 +1046,8 @@ void GmpProtocol::storeConfigWidget() configWidget(); + configForm->update(); + setFieldData(kType, configForm->msgTypeCombo->currentValue()); setFieldData(kMldMrt, configForm->maxResponseTime->text()); setFieldData(kIsOverrideChecksum, @@ -901,27 +1069,21 @@ void GmpProtocol::storeConfigWidget() list.append(configForm->sourceList->item(i)->text()); setFieldData(kSources, list); - // XXX: sourceCount should be AFTER sources + // sourceCount should be AFTER sources setFieldData(kIsOverrideSourceCount, configForm->overrideSourceCount->isChecked()); setFieldData(kSourceCount, configForm->sourceCount->text()); - // TODO: Group Records + QVariantList grpList; + for (int i = 0; i < configForm->groupList->count(); i++) + { + grpList.append(configForm->groupList->item(i)->data(Qt::UserRole) + .toMap()); + } + setFieldData(kGroupRecords, grpList); - // XXX: groupRecordCount should be AFTER groupRecords + // groupRecordCount should be AFTER groupRecords setFieldData(kIsOverrideGroupRecordCount, configForm->overrideGroupRecordCount->isChecked()); setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); -#if 0 - setFieldData(kA, configForm->gmpA->text()); - setFieldData(kB, configForm->gmpB->text()); - - setFieldData(kPayloadLength, configForm->gmpPayloadLength->text()); - setFieldData(kIs_override_checksum, - configForm->isChecksumOverride->isChecked()); - setFieldData(kChecksum, configForm->gmpChecksum->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(kX, configForm->gmpX->text()); - setFieldData(kY, configForm->gmpY->text()); -#endif } diff --git a/common/gmp.h b/common/gmp.h index c382005..1a9fc7e 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -54,6 +54,7 @@ Gmp Protocol Frame Format - +-----+------+------+------+------+------+ Figures in brackets represent field width in bits */ +class GmpProtocol; class GmpConfigForm : public QWidget, public Ui::Gmp { @@ -61,6 +62,7 @@ class GmpConfigForm : public QWidget, public Ui::Gmp public: GmpConfigForm(QWidget *parent = 0); ~GmpConfigForm(); + void update(); protected: QString _defaultSourceIp; private slots: diff --git a/common/gmp.ui b/common/gmp.ui index 6e1f0d8..82e84f1 100755 --- a/common/gmp.ui +++ b/common/gmp.ui @@ -435,7 +435,14 @@ - + + + true + + + QAbstractItemView::InternalMove + + @@ -580,7 +587,14 @@ - + + + true + + + QAbstractItemView::InternalMove + + @@ -646,7 +660,7 @@ - Length + Length (x4) diff --git a/common/igmp.cpp b/common/igmp.cpp index f7c0329..09a9feb 100644 --- a/common/igmp.cpp +++ b/common/igmp.cpp @@ -212,32 +212,47 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, { switch(attrib) { - case FieldName: - return QString("Group List"); case FieldValue: - return QVariant(); // FIXME + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList l; + for (int j = 0; j < rec.sources_size(); j++) + l.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = l; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } case FieldFrameValue: { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); QByteArray fv; + for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); - QByteArray rv; + QByteArray rv = list.at(i).toByteArray(); - rv.resize(4 + 4 + 4*data.group_records(i).sources_size() - + data.group_records(i).aux_data().size()); - rv[0] = rec.type(); - rv[1] = rec.is_override_aux_data_length() ? - rec.aux_data_length() : rec.aux_data().size(); - if (rec.is_override_source_count()) - qToBigEndian(rec.source_count(),(uchar*)(rv.data()+2)); - else - qToBigEndian(rec.sources_size(),(uchar*)(rv.data()+2)); + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); qToBigEndian(rec.group_address().v4(), - (uchar*)(fv.data()+4)); + (uchar*)(rv.data()+4)); for (int j = 0; j < rec.sources_size(); j++) - qToBigEndian(rec.sources(j),(uchar*)(rv.data()+8+4*j)); - rv.append(QString().fromStdString(rec.aux_data()).toUtf8()); + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } fv.append(rv); } @@ -245,38 +260,16 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, } case FieldTextValue: { - QStringList list; + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); QString str; - str.append("Type: "); - switch(rec.type()) - { - case OstProto::Gmp::GroupRecord::kIsInclude: - str.append("IS_INCLUDE"); break; - case OstProto::Gmp::GroupRecord::kIsExclude: - str.append("IS_EXCLUDE"); break; - case OstProto::Gmp::GroupRecord::kToInclude: - str.append("TO_INCLUDE"); break; - case OstProto::Gmp::GroupRecord::kToExclude: - str.append("TO_EXCLUDE"); break; - case OstProto::Gmp::GroupRecord::kAllowNew: - str.append("ALLOW_NEW"); break; - case OstProto::Gmp::GroupRecord::kBlockOld: - str.append("BLOCK_OLD"); break; - default: - str.append("UNKNOWN"); break; - } - str.append(QString("; AuxLen: %1").arg( - rec.is_override_aux_data_length() ? - rec.aux_data_length() : rec.aux_data().size())); - str.append(QString("; Source Count: %1").arg( - rec.is_override_source_count() ? - rec.source_count(): rec.sources_size())); - str.append(QString("; Group: %1").arg( + str.append(QString("Group: %1").arg( QHostAddress(rec.group_address().v4()).toString())); str.append("; Sources: "); @@ -284,11 +277,11 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, for (int j = 0; j < rec.sources_size(); j++) l.append(QHostAddress(rec.sources(j).v4()).toString()); str.append(l.join(", ")); - str.append(QString().fromStdString(rec.aux_data())); - list.append(str); + recStr.replace("XXX", str); + list.replace(i, recStr); } - return list.join("\n"); + return list.join("\n").insert(0, "\n"); } default: break; @@ -335,8 +328,30 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value, } case kGroupRecords: - //TODO + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + break; + } default: isOk = GmpProtocol::setFieldData(index, value, attrib); From d8b9844dacbe27cac6e70d26c1151533fa5b0a48 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 15 Sep 2010 20:58:10 +0530 Subject: [PATCH 096/294] Changed QProcess()::start() to the overloaded version which plays nice with spaces in the pathname Fixes issue 9 --- client/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 87ae5d5..291865f 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -55,7 +55,7 @@ MainWindow::MainWindow(QWidget *parent) localServer_ = new QProcess(this); localServer_->setProcessChannelMode(QProcess::ForwardedChannels); - localServer_->start(serverApp); + localServer_->start(serverApp, QStringList()); // TODO: waitForReadyRead() is a kludge till we implement auto-retry! localServer_->waitForReadyRead(1000); From c5fbceebb48dff7a1aaa8322e4ec5465c2046472 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 1 Oct 2010 21:29:55 +0530 Subject: [PATCH 097/294] At port init, the sendqueue dirty flag should be false not true --- server/abstractport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 954278b..5a08911 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -34,7 +34,7 @@ AbstractPort::AbstractPort(int id, const char *device) data_.set_is_exclusive_control(false); - isSendQueueDirty_ = true; + isSendQueueDirty_ = false; linkState_ = OstProto::LinkStateUnknown; memset((void*) &stats_, 0, sizeof(stats_)); From 90fda499dd370bdfccd4459af6c4b120adfc5224 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 18 Oct 2010 17:25:27 +0530 Subject: [PATCH 098/294] RPC now uses stream instead of an array for protobuf parsing and serialization. This change removes the limit on the byte size of a protobuf. Because we are now using a new protobuf API - ParseFromBoundedZeroCopyStream(), the protobuf version has to be >= 2.2.0 Fixes issue 14 --- client/ostinato.pro | 2 +- rpc/pbqtio.h | 42 ++++++++++++++++++++++++++++++++++++++++++ rpc/pbrpc.pro | 2 +- rpc/pbrpcchannel.cpp | 44 +++++++++++++++++++++++--------------------- rpc/pbrpcchannel.h | 4 ++++ rpc/pbrpccommon.h | 2 -- rpc/rpcserver.cpp | 37 ++++++++++++++++++++++++------------- rpc/rpcserver.h | 3 +++ 8 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 rpc/pbqtio.h diff --git a/client/ostinato.pro b/client/ostinato.pro index 299ecc5..e61730e 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -5,7 +5,6 @@ win32:RC_FILE = ostinato.rc macx:ICON = icons/logo.icns QT += network script INCLUDEPATH += "../rpc/" "../common/" -LIBS += -lprotobuf win32 { CONFIG(debug, debug|release) { LIBS += -L"../common/debug" -lostproto @@ -25,6 +24,7 @@ win32 { LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } +LIBS += -lprotobuf RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro index 27ec61b..f53fa81 100644 --- a/rpc/pbrpc.pro +++ b/rpc/pbrpc.pro @@ -3,5 +3,5 @@ CONFIG += qt staticlib QT += network DEFINES += HAVE_REMOTE LIBS += -lprotobuf -HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h pbqtio.h SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 47f1611..7c7789e 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -18,6 +18,7 @@ along with this program. If not, see */ #include "pbrpcchannel.h" +#include "pbqtio.h" #include @@ -34,6 +35,13 @@ PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) mServerPort = port; mpSocket = new QTcpSocket(this); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + // FIXME: Not quite sure why this ain't working! // QMetaObject::connectSlotsByName(this); @@ -53,6 +61,8 @@ PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) PbRpcChannel::~PbRpcChannel() { + delete inStream; + delete outStream; delete mpSocket; } @@ -84,7 +94,7 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char msgBuf[MSGBUF_SIZE]; + char msgBuf[PB_HDR_SIZE]; char* const msg = &msgBuf[0]; int len; bool ret; @@ -128,9 +138,6 @@ void PbRpcChannel::CallMethod( this->response=response; isPending = true; - ret = req->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); - Q_ASSERT(ret == true); - len = req->ByteSize(); *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id @@ -141,15 +148,18 @@ void PbRpcChannel::CallMethod( { qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, PB_HDR_SIZE + len, req->DebugString().c_str()); - BUFDUMP(msg, PB_HDR_SIZE + len); + BUFDUMP(msg, PB_HDR_SIZE); } - mpSocket->write(msg, PB_HDR_SIZE + len); + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); } void PbRpcChannel::on_mpSocket_readyRead() { - uchar msg[MSGBUF_SIZE]; + uchar msg[PB_HDR_SIZE]; uchar *p = (uchar*) &msg; int msgLen; static bool parsing = false; @@ -210,31 +220,20 @@ void PbRpcChannel::on_mpSocket_readyRead() if (!isPending) { qDebug("not waiting for response"); - goto _error_exit; + goto _error_exit2; } if (pendingMethodId != method) { qDebug("invalid method id %d (expected = %d)", method, pendingMethodId); - goto _error_exit; + goto _error_exit2; } break; } case PB_MSG_TYPE_RESPONSE: - // Wait till we have the entire message - if (mpSocket->bytesAvailable() < len) - { - qDebug("client: not enough data available for a complete msg"); - return; - } - - msgLen = mpSocket->read((char*)msg, sizeof(msg)); - - Q_ASSERT((unsigned) msgLen == len); - //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); //BUFDUMP(msg, msgLen); @@ -251,7 +250,8 @@ void PbRpcChannel::on_mpSocket_readyRead() goto _error_exit; } - response->ParseFromArray((void*) msg, len); + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); // Avoid printing stats if (method != 13) @@ -295,6 +295,8 @@ void PbRpcChannel::on_mpSocket_readyRead() return; _error_exit: + inStream->Skip(len); +_error_exit2: parsing = false; qDebug("client(%s) discarding received msg", __FUNCTION__); return; diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h index 682c553..e3f9096 100644 --- a/rpc/pbrpcchannel.h +++ b/rpc/pbrpcchannel.h @@ -23,6 +23,7 @@ along with this program. If not, see #include #include +#include #include #include #include @@ -64,6 +65,9 @@ class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel quint16 mServerPort; QTcpSocket *mpSocket; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + public: PbRpcChannel(QHostAddress ip, quint16 port); ~PbRpcChannel(); diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index 0f4acf9..e1fbdf9 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -36,6 +36,4 @@ along with this program. If not, see #define PB_MSG_TYPE_RESPONSE 2 #define PB_MSG_TYPE_BINBLOB 3 -#define MSGBUF_SIZE 4096 - #endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index d48de21..2d1bd63 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -19,6 +19,7 @@ along with this program. If not, see //#include "pbhelper.h" #include "rpcserver.h" +#include "pbqtio.h" #include @@ -29,6 +30,9 @@ RpcServer::RpcServer() service = NULL; + inStream = NULL; + outStream = NULL; + isPending = false; pendingMethodId = -1; // don't care as long as isPending is false } @@ -71,7 +75,7 @@ void RpcServer::done(PbRpcController *controller) { google::protobuf::Message *response = controller->response(); QIODevice *blob; - char msgBuf[MSGBUF_SIZE]; + char msgBuf[PB_HDR_SIZE]; char* const msg = &msgBuf[0]; int len; @@ -116,8 +120,6 @@ void RpcServer::done(PbRpcController *controller) goto _exit; } - response->SerializeToArray((void*)(msg+PB_HDR_SIZE), sizeof(msgBuf)-PB_HDR_SIZE); - len = response->ByteSize(); *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type @@ -132,7 +134,9 @@ void RpcServer::done(PbRpcController *controller) //BUFDUMP(msg, len + 8); } - clientSock->write(msg, PB_HDR_SIZE + len); + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); _exit: delete controller; @@ -159,6 +163,12 @@ void RpcServer::when_newConnection() qDebug("accepting new connection from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); connect(clientSock, SIGNAL(readyRead()), this, SLOT(when_dataAvail())); @@ -177,6 +187,9 @@ void RpcServer::when_disconnected() clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); + delete inStream; + delete outStream; + clientSock->deleteLater(); clientSock = NULL; } @@ -189,7 +202,7 @@ void RpcServer::when_error(QAbstractSocket::SocketError socketError) void RpcServer::when_dataAvail() { - uchar msg[MSGBUF_SIZE]; + uchar msg[PB_HDR_SIZE]; int msgLen; static bool parsing = false; static quint16 type, method; @@ -215,12 +228,6 @@ void RpcServer::when_dataAvail() parsing = true; } - if (clientSock->bytesAvailable() < len) - return; - - msgLen = clientSock->read((char*)msg, sizeof(msg)); - Q_ASSERT((unsigned) msgLen == len); - if (type != PB_MSG_TYPE_REQUEST) { qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, @@ -247,7 +254,9 @@ void RpcServer::when_dataAvail() req = service->GetRequestPrototype(methodDesc).New(); resp = service->GetResponsePrototype(methodDesc).New(); - req->ParseFromArray((void*)msg, len); + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + if (!req->IsInitialized()) { qWarning("Missing required fields in request"); @@ -256,7 +265,7 @@ void RpcServer::when_dataAvail() delete req; delete resp; - goto _error_exit; + goto _error_exit2; } //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, //resp->DebugString().c_str()); @@ -273,6 +282,8 @@ void RpcServer::when_dataAvail() return; _error_exit: + inStream->Skip(len); +_error_exit2: parsing = false; qDebug("server(%s): discarding msg from client", __FUNCTION__); return; diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 1e9c93e..76f179a 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -23,6 +23,7 @@ along with this program. If not, see #include #include #include +#include #include #include @@ -39,6 +40,8 @@ class RpcServer : public QObject QTcpSocket *clientSock; ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; bool isPending; int pendingMethodId; From 3aac690ed869e564f3be1106d2bf4e0152da22a1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 25 Oct 2010 19:27:23 +0530 Subject: [PATCH 099/294] While appending streams, the streamId and ordinalId were getting overwritten. Port::newStreamAt() now takes an additional parameter - pointer to stream (default NULL) which is used when appending streams Fixes Issue 21 --- client/port.cpp | 8 +++++--- client/port.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 6b5e13e..289e44a 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -65,13 +65,16 @@ void Port::reorderStreamsByOrdinals() qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); } -bool Port::newStreamAt(int index) +bool Port::newStreamAt(int index, OstProto::Stream const *stream) { Stream *s = new Stream; if (index > mStreams.size()) return false; + if (stream) + s->protoDataCopyFrom(*stream); + s->setId(newStreamId()); mStreams.insert(index, s); updateStreamOrdinalsFromIndex(); @@ -235,8 +238,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error) for (int i = 0; i < streams.stream_size(); i++) { - newStreamAt(mStreams.size()); - streamByIndex(mStreams.size()-1)->protoDataCopyFrom(streams.stream(i)); + newStreamAt(mStreams.size(), &streams.stream(i)); } emit streamListChanged(mPortGroupId, mPortId); diff --git a/client/port.h b/client/port.h index e4bb9c9..2fbb9a0 100644 --- a/client/port.h +++ b/client/port.h @@ -99,7 +99,7 @@ public: //! Used by StreamModel //@{ - bool newStreamAt(int index); + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); bool deleteStreamAt(int index); //@} From fbde299478b1d704b0d1c5f1f3528b7d8d8af3d1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 3 Nov 2010 19:39:36 +0530 Subject: [PATCH 100/294] Added HexDump Protocol --- client/ostinato.pro | 1 + client/streamconfigdialog.cpp | 1 + client/streamconfigdialog.ui | 154 +++++---- common/hexdump.cpp | 263 +++++++++++++++ common/hexdump.h | 89 +++++ common/hexdump.proto | 32 ++ common/hexdump.ui | 76 +++++ common/ostproto.pro | 5 + common/protocol.proto | 1 + common/protocolmanager.cpp | 3 + extra/qhexedit2/qhexedit2.pro | 8 + extra/qhexedit2/src/license.txt | 502 +++++++++++++++++++++++++++++ extra/qhexedit2/src/qhexedit.cpp | 106 ++++++ extra/qhexedit2/src/qhexedit.h | 145 +++++++++ extra/qhexedit2/src/qhexedit_p.cpp | 414 ++++++++++++++++++++++++ extra/qhexedit2/src/qhexedit_p.h | 82 +++++ server/drone.pro | 1 + 17 files changed, 1803 insertions(+), 80 deletions(-) create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h diff --git a/client/ostinato.pro b/client/ostinato.pro index e61730e..fa69080 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -25,6 +25,7 @@ win32 { POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 993cd1c..cc47dd7 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -243,6 +243,7 @@ void StreamConfigDialog::setupUiExtra() #else bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); #endif /* diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 43c56b6..1f0a838 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -8,7 +8,7 @@ 0 0 - 626 + 634 507 @@ -173,7 +173,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 584 + 592 269 @@ -181,7 +181,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Simple - + L1 @@ -220,7 +220,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + true @@ -403,17 +403,17 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - + + true - Payload + L5 - + None @@ -423,17 +423,17 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - Pattern - - + + false + + Text + - + false @@ -445,7 +445,49 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + true @@ -517,59 +559,17 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - + + true - VLAN - - - false - - - false + Payload - - - Untagged - - - true - - - - - - - Tagged - - - - - - - Stacked - - - - - - - - - - true - - - L5 - - - - + None @@ -579,17 +579,24 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - false - + - Text + Pattern + + + false - + + + Hex Dump + + + + + false @@ -601,19 +608,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..f579430 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,263 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +#include + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +HexDumpProtocol::~HexDumpProtocol() +{ + delete configForm; +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + +QWidget* HexDumpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new HexDumpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void HexDumpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hexEdit->setData( + fieldData(hexDump_content, FieldValue).toByteArray()); + configForm->padUntilEnd->setChecked( + fieldData(hexDump_pad_until_end, FieldValue).toBool()); +} + +void HexDumpProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(hexDump_content, configForm->hexEdit->data()); + setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..f5b6932 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "hexdump.pb.h" +#include "ui_hexdump.h" + +#include "abstractprotocol.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpConfigForm : public QWidget, public Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +class HexDumpProtocol : public AbstractProtocol +{ +public: + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + OstProto::HexDump data; + HexDumpConfigForm *configForm; + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
qhexedit.h
+ 1 +
+
+ + +
diff --git a/common/ostproto.pro b/common/ostproto.pro index 68f1cc6..28d5dc7 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -1,6 +1,7 @@ TEMPLATE = lib CONFIG += qt staticlib QT += network script +INCLUDEPATH += "../extra/qhexedit2/src" LIBS += \ -lprotobuf FORMS += \ @@ -19,6 +20,7 @@ FORMS += \ udp.ui \ textproto.ui \ userscript.ui \ + hexdump.ui \ sample.ui PROTOS += \ protocol.proto \ @@ -46,6 +48,7 @@ PROTOS += \ udp.proto \ textproto.proto \ userscript.proto \ + hexdump.proto \ sample.proto HEADERS += \ abstractprotocol.h \ @@ -78,6 +81,7 @@ HEADERS += \ udp.h \ textproto.h \ userscript.h \ + hexdump.h \ sample.h SOURCES += \ abstractprotocol.cpp \ @@ -103,6 +107,7 @@ SOURCES += \ udp.cpp \ textproto.cpp \ userscript.cpp \ + hexdump.cpp \ sample.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocol.proto b/common/protocol.proto index 3510e2b..25b0fbb 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -86,6 +86,7 @@ message Protocol { kPayloadFieldNumber = 101; kSampleFieldNumber = 102; kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; kEth2FieldNumber = 200; kDot3FieldNumber = 201; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 0910e1a..b256135 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -43,6 +43,7 @@ along with this program. If not, see #include "udp.h" #include "textproto.h" #include "userscript.h" +#include "hexdump.h" #include "sample.h" ProtocolManager *OstProtocolManager; @@ -99,6 +100,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); registerProtocol(OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadProtocol::createInstance); diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..c7b9989 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/qhexedit.h \ + src/qhexedit_p.h + +SOURCES = src/qhexedit.cpp \ + src/qhexedit_p.cpp diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..a295110 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,106 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(currentAddress(int)), this, SIGNAL(currentAddress(int))); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..2b94581 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,145 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.4.3 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text data. +There are sip configuration files included, so it is easy to create bindings +for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the +mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f) +you will change the data. Changed data is highlighted and can be accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and +insert data. In this case the size of data() increases. It is also possible to delete +bytes under the cursor, here the size of data decreases. + +There are some limitations: The size of data has in general to be below 10 megabytes, +otherwise the scroll sliders ard not shown and you can't scroll any more. Copy and +paste functionality is perhaps a subject of a later release. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwritem mode the user will overwrite existing data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + */ + void remove(int pos, int len=1); + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + void setFont(const QFont &); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + /*! \endcond docNever */ + +public slots: + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddress(int address); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..30f0660 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,414 @@ +#include + +#include "qhexedit_p.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setAddressAreaColor(QColor(Qt::lightGray).lighter(110)); + setHighlightingColor(QColor(Qt::yellow).lighter(160)); + + setFont(QFont("Mono", 10)); + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + + _cursorTimer.setInterval(500); + _cursorTimer.start(); + + setFocusPolicy(Qt::StrongFocus); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _addressOffset = offset; + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _addressOffset; +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _data = data; + _originalData = data; + adjust(); + setCursorPos(0); + setFocus(); +} + +QByteArray QHexEditPrivate::data() +{ + return _data; +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + if (overwriteMode != _overwriteMode) + { + emit overwriteModeChanged(overwriteMode); + _overwriteMode = overwriteMode; + adjust(); + } +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _originalData.insert(i, ba); +} + +void QHexEditPrivate::insert(int i, char ch) +{ + _data.insert(i, ch); + _originalData.insert(i, ch); +} + +void QHexEditPrivate::remove(int index, int len) +{ + _data.remove(index, len); + _originalData.remove(index, len); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + if ((addressWidth >= 0) and (addressWidth<=6)) + { + _addressNumbers = addressWidth; + adjust(); + setCursorPos(_cursorPosition); + } +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + bool down = false; + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + // calc address + + + // insert char + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + adjust(); + } + QByteArray hexValue = _data.mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + _data.replace(posBa, 1, QByteArray().fromHex(hexValue)); + emit dataChanged(); + + setCursorPos(_cursorPosition + 1); + down = true; + } + + // delete char + if (event->matches(QKeySequence::Delete)) + remove(posBa); + if (event->key() == Qt::Key_Backspace) + { + remove(posBa - 1); + setCursorPos(_cursorPosition - 2); + } + + // handle other function keys + if (event->key() == Qt::Key_Insert) + setOverwriteMode(!_overwriteMode); + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + down = true; + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + setCursorPos(_cursorPosition - 1); + if (event->matches(QKeySequence::MoveToStartOfLine)) + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + if (event->matches(QKeySequence::MoveToEndOfLine)) + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + if (event->matches(QKeySequence::MoveToPreviousLine)) + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToPreviousPage)) + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToStartOfDocument)) + setCursorPos(0); + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + down = true; + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_data.size() * 2); + down = true; + } + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + down = true; + } + + // when we move downwards, we have to go a little further + if (down) + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3 + _charHeight); + else + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3); + update(); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + setCursorPos(event->pos()); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _data.size()) + lastLineIdx = _data.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _addressOffset, _realAddressNumbers, 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + painter.setBackground(highLighted); + painter.setBackgroundMode(Qt::TransparentMode); + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + // hilight diff bytes + if (_highlighting) + { + int posBa = lineIdx + colIdx; + if (posBa >= _originalData.size()) + painter.setBackgroundMode(Qt::TransparentMode); + else + if (_data[posBa] == _originalData[posBa]) + painter.setBackgroundMode(Qt::TransparentMode); + else + painter.setBackgroundMode(Qt::OpaqueMode); + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + } + } + painter.setBackgroundMode(Qt::TransparentMode); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray ascii = _data.mid(lineIdx, BYTES_PER_LINE); + for (int idx=0; idx < ascii.size(); idx++) + if (((char)ascii[idx] < 0x20) or ((char)ascii[idx] > 0x7e)) + ascii[idx] = '.'; + painter.drawText(_xPosAscii, yPos, ascii); + } + } + + // paint cursor + if ((_data.size() > 0) and _blink) + painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText)); +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_data.size() * 2 - 1)) + position = _data.size() * 2 - 1; + } else { + if (position > (_data.size() * 2)) + position = _data.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddress(_cursorPosition/2); +} + +void QHexEditPrivate::setCursorPos(QPoint pos) +{ + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = (pos.y() / _charHeight) * 2 * BYTES_PER_LINE; + setCursorPos(x + y); + } +} + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _realAddressNumbers *_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + if (_overwriteMode) + _cursorWidth = _charWidth; + else + _cursorWidth = 2; + _cursorHeight = _charHeight - 3; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_data.size()/16 + 1) * _charHeight) + 3); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..c422c58 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,82 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressOffset(int offset); + int addressOffset(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void insert(int i, const QByteArray & ba); + void insert(int i, char ch); + void remove(int index, int len=1); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + +signals: + void currentAddress(int address); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *event); + void setCursorPos(QPoint pos); + void setCursorPos(int position); + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QByteArray _data; + QByteArray _originalData; + QColor _highlightingColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + + bool _blink; + bool _addressArea; + bool _asciiArea; + bool _highlighting; + bool _overwriteMode; + + int _addressNumbers, _realAddressNumbers; + int _addressOffset; + int _charWidth, _charHeight; + int _cursorX, _cursorY, _cursorWidth, _cursorHeight, _cursorPosition; + int _xPosAdr, _xPosHex, _xPosAscii; +}; + +/** \endcond docNever */ + +#endif + diff --git a/server/drone.pro b/server/drone.pro index b2b25d1..160973b 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -25,6 +25,7 @@ win32 { POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 RESOURCES += drone.qrc HEADERS += drone.h FORMS += drone.ui From a6c1166a788be90c253ac5a7630fdf6c906a05eb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 3 Nov 2010 20:21:57 +0530 Subject: [PATCH 101/294] Added missing qmake project file for extra libraries --- extra/extra.pro | 3 +++ ost.pro | 1 + 2 files changed, 4 insertions(+) create mode 100644 extra/extra.pro diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/ost.pro b/ost.pro index bfe4e1a..0f9d987 100644 --- a/ost.pro +++ b/ost.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs CONFIG += ordered SUBDIRS = \ + extra \ rpc/pbrpc.pro \ common/ostproto.pro \ server/drone.pro \ From 599e5919072a2289c470d59d61d7e88513430457 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 4 Nov 2010 23:05:26 +0530 Subject: [PATCH 102/294] TextProtocol now allows specifying the end-of-line symbol Fixes Issue 18 --- common/fileformat.h | 2 +- common/textproto.cpp | 36 +++++++++++++++++++++++++++++++++++- common/textproto.h | 4 +++- common/textproto.proto | 7 +++++++ common/textproto.ui | 35 ++++++++++++++++++++++++++++++++--- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/common/fileformat.h b/common/fileformat.h index d181567..acb8337 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -49,7 +49,7 @@ private: // Native file format version static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 1; - static const uint kFileFormatVersionRevision = 0; + static const uint kFileFormatVersionRevision = 1; void initFileMetaData(OstProto::FileMetaData &metaData); }; diff --git a/common/textproto.cpp b/common/textproto.cpp index c86f7ff..9834530 100644 --- a/common/textproto.cpp +++ b/common/textproto.cpp @@ -107,6 +107,7 @@ AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const break; case textProto_portNum: + case textProto_eol: case textProto_encoding: flags &= ~FrameField; flags |= MetaField; @@ -136,8 +137,18 @@ QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, case FieldTextValue: return QString().fromStdString(data.text()); case FieldFrameValue: + { + QString text; Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); - return QString().fromStdString(data.text()).toUtf8(); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } default: break; } @@ -157,6 +168,17 @@ QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, } break; } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } case textProto_encoding: { switch(attrib) @@ -200,6 +222,15 @@ bool TextProtocol::setFieldData(int index, const QVariant &value, data.set_port_num(portNum); break; } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } case textProto_encoding: { uint enc = value.toUInt(&isOk); @@ -243,6 +274,8 @@ void TextProtocol::loadConfigWidget() configForm->portNumCombo->setValue( fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->eolCombo->setCurrentIndex( + fieldData(textProto_eol, FieldValue).toUInt()); configForm->encodingCombo->setCurrentIndex( fieldData(textProto_encoding, FieldValue).toUInt()); configForm->protoText->setText( @@ -254,6 +287,7 @@ void TextProtocol::storeConfigWidget() configWidget(); setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); setFieldData(textProto_text, configForm->protoText->toPlainText()); diff --git a/common/textproto.h b/common/textproto.h index 57b71cc..1ec5fc0 100644 --- a/common/textproto.h +++ b/common/textproto.h @@ -27,7 +27,8 @@ along with this program. If not, see /* TextProtocol Protocol Frame Format - - specified text encoded with the specified encoding + specified text with the specified line ending and encoded with the + specified encoding */ class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol @@ -50,6 +51,7 @@ private: // Meta Fields textProto_portNum, + textProto_eol, textProto_encoding, textProto_fieldCount diff --git a/common/textproto.proto b/common/textproto.proto index 12e5b4e..e20e496 100644 --- a/common/textproto.proto +++ b/common/textproto.proto @@ -26,10 +26,17 @@ message TextProtocol { enum TextEncoding { kUtf8 = 0; } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } optional uint32 port_num = 1 [default = 80]; optional TextEncoding encoding = 2 [default = kUtf8]; optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; } extend Protocol { diff --git a/common/textproto.ui b/common/textproto.ui index b3cf278..f6996aa 100644 --- a/common/textproto.ui +++ b/common/textproto.ui @@ -5,7 +5,7 @@ 0 0 - 493 + 535 300 @@ -33,7 +33,36 @@
+ + + + Line Ending + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + Encode as @@ -43,7 +72,7 @@ - + @@ -58,7 +87,7 @@
- + false From dbc7409616b535e106ad4c20dd680efac14d2f25 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 6 Nov 2010 23:29:44 +0530 Subject: [PATCH 103/294] Review and rework of GMP and IGMP --- client/ostinato.pro | 2 +- common/gmp.cpp | 131 ++++++++++++++++------------------- common/gmp.h | 130 +++++++++++++++++++--------------- common/gmp.proto | 38 +++++----- common/gmp.ui | 16 +++-- common/igmp.cpp | 109 ++++++++++------------------- common/igmp.h | 7 ++ common/iputils.h | 70 +++++++++++++++++++ common/ipv4addressdelegate.h | 58 ++++++++++++++++ common/ostproto.pro | 1 + 10 files changed, 339 insertions(+), 223 deletions(-) create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h diff --git a/client/ostinato.pro b/client/ostinato.pro index 299ecc5..e61730e 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -5,7 +5,6 @@ win32:RC_FILE = ostinato.rc macx:ICON = icons/logo.icns QT += network script INCLUDEPATH += "../rpc/" "../common/" -LIBS += -lprotobuf win32 { CONFIG(debug, debug|release) { LIBS += -L"../common/debug" -lostproto @@ -25,6 +24,7 @@ win32 { LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } +LIBS += -lprotobuf RESOURCES += ostinato.qrc HEADERS += \ dumpview.h \ diff --git a/common/gmp.cpp b/common/gmp.cpp index 559fa74..cddef4f 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -22,11 +22,14 @@ along with this program. If not, see #include #include +QHash GmpProtocol::frameFieldCountMap; + GmpConfigForm::GmpConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); + // TODO: this should be in subclass msgTypeCombo->setValueMask(0xFF); msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); @@ -70,14 +73,14 @@ void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) case kIgmpV3Query: case kMldV2Query: asmGroup->hide(); - ssmWidget->setCurrentIndex(0); + ssmWidget->setCurrentIndex(kSsmQueryPage); ssmWidget->show(); break; case kIgmpV3Report: case kMldV2Report: asmGroup->hide(); - ssmWidget->setCurrentIndex(1); + ssmWidget->setCurrentIndex(kSsmReportPage); ssmWidget->show(); break; @@ -88,6 +91,14 @@ void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) } } +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + void GmpConfigForm::on_addSource_clicked() { QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); @@ -113,13 +124,14 @@ void GmpConfigForm::on_addGroupRecord_clicked() QListWidgetItem *item = new QListWidgetItem; grpRec["groupRecordType"] = defRec.type()-1; - grpRec["groupRecordAddress"] = _defaultSourceIp; - grpRec["overrideGroupRecordSourceCount"] = defRec.is_override_source_count(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); grpRec["groupRecordSourceCount"] = defRec.source_count(); grpRec["groupRecordSourceList"] = QStringList(); grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); grpRec["auxDataLength"] = defRec.aux_data_length(); - grpRec["auxData"] = QString().fromStdString(defRec.aux_data()); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); item->setData(Qt::UserRole, grpRec); item->setText(QString("%1: %2") @@ -167,7 +179,7 @@ void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); rec["auxDataLength"] = auxDataLength->text().toUInt(); - rec["auxData"] = auxData->text(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); previous->setData(Qt::UserRole, rec); previous->setText(QString("%1: %2") @@ -196,7 +208,7 @@ _load_current_record: rec["groupRecordSourceCount"].toUInt())); overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); - auxData->setText(rec["auxData"].toString()); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); _exit: groupRecord->setEnabled(current != NULL); @@ -254,46 +266,24 @@ int GmpProtocol::fieldCount() const int GmpProtocol::frameFieldCount() const { - int count = 0; + int type = msgType(); - // TODO: optimize!!!!! + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; for (int i = 0; i < FIELD_COUNT; i++) { if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) count++; } + frameFieldCountMap.insert(type, count); return count; -#if 0 - switch(msgType()) - { - // IGMP - case kIgmpV1Query: - case kIgmpV1Report: - case kIgmpV2Query: - case kIgmpV2Report: - case kIgmpV2Leave: - return FIELD_COUNT_ASM_ALL; - - case kIgmpV3Query: - return FIELD_COUNT_SSM_QUERY; - case kIgmpV3Report: - return FIELD_COUNT_SSM_REPORT; - - // MLD - case kMldV1Query: - case kMldV1Report: - case kMldV1Done: - return FIELD_COUNT_ASM_ALL; - - case kMldV2Query: - return FIELD_COUNT_SSM_QUERY; - case kMldV2Report: - return FIELD_COUNT_SSM_REPORT; - - default: - return FIELD_COUNT_ASM_ALL; - } -#endif } AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const @@ -316,6 +306,7 @@ AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const break; case kMldMrt: case kMldRsvd: + // MLD subclass should handle suitably break; case kGroupAddress: @@ -404,13 +395,21 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, } case kChecksum: { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + quint16 cksum = data.is_override_checksum() ? data.checksum() : checksum(streamIndex); switch(attrib) { - case FieldName: - return QString("Checksum"); case FieldValue: return cksum; case FieldFrameValue: @@ -423,14 +422,13 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, } case FieldTextValue: return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); - case FieldBitSize: - return 16; default: break; } break; } case kMldMrt: + case kMldRsvd: // XXX: Present only in MLD - hence handled by the mld subclass break; @@ -440,7 +438,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, case kRsvd1: { - int rsvd = 0; + quint8 rsvd = 0; switch(attrib) { @@ -513,8 +511,8 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, return QString("%1").arg(qqi); case FieldFrameValue: { - quint8 qqic = qqi; // TODO: derive code from qqi - return QByteArray(1, char(qqic)); + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); } default: break; @@ -628,7 +626,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, rec.is_override_aux_data_length(); grpRec["auxDataLength"] = rec.aux_data_length(); grpRec["auxData"] = QByteArray().append( - QString::fromStdString(rec.aux_data())).toHex(); + QString::fromStdString(rec.aux_data())); grpRecords.append(grpRec); } @@ -641,26 +639,23 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, { OstProto::Gmp::GroupRecord rec = data.group_records(i); QByteArray rv; + quint16 srcCount; rv.resize(4); rv[0] = rec.type(); rv[1] = rec.is_override_aux_data_length() ? rec.aux_data_length() : rec.aux_data().size()/4; + if (rec.is_override_source_count()) - { - qToBigEndian(quint16(rec.source_count()), - (uchar*)(rv.data()+2)); - } + srcCount = rec.source_count(); else - { - qToBigEndian(quint16(rec.sources_size()), - (uchar*)(rv.data()+2)); - } + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); // group_address => subclass responsibility // source list => subclass responsibility - rv.append(QString().fromStdString(rec.aux_data()).toUtf8()); + rv.append(QString().fromStdString(rec.aux_data())); fv.append(rv); } @@ -823,6 +818,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, } case kGroupAddress: // XXX: Handled by subclass + isOk = false; break; case kRsvd1: isOk = false; @@ -843,7 +839,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, } case kQqic: { - uint qqi = value.toUInt(&isOk); // TODO: QQIC or QQI?? + uint qqi = value.toUInt(&isOk); if (isOk) data.set_qqi(qqi); break; @@ -857,6 +853,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, } case kSources: // XXX: Handled by subclass + isOk = false; break; case kRsvd2: isOk = false; @@ -889,8 +886,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, rec->set_is_override_aux_data_length( grpRec["overrideAuxDataLength"].toBool()); rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); - QByteArray ba = QByteArray::fromHex( - grpRec["auxData"].toByteArray()); + QByteArray ba = grpRec["auxData"].toByteArray(); // pad to word boundary if (ba.size() % 4) ba.append(QByteArray(4 - (ba.size() % 4), char(0))); @@ -956,12 +952,6 @@ _exit: return isOk; } -/*! - TODO: Return the protocol frame size in bytes\n - - If your protocol has a fixed size - you don't need to reimplement this; the - base class implementation is good enough -*/ int GmpProtocol::protocolFrameSize(int streamIndex) const { // TODO: Calculate to reduce processing cost @@ -987,8 +977,7 @@ void GmpProtocol::loadConfigWidget() configWidget(); configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); - configForm->maxResponseTime->setText(QString("%1").arg( - data.max_response_time())); + // XXX: configForm->maxResponseTime set by subclass configForm->overrideChecksum->setChecked( fieldData(kIsOverrideChecksum, FieldValue).toBool()); configForm->checksum->setText(uintToHexStr( @@ -1049,7 +1038,7 @@ void GmpProtocol::storeConfigWidget() configForm->update(); setFieldData(kType, configForm->msgTypeCombo->currentValue()); - setFieldData(kMldMrt, configForm->maxResponseTime->text()); + // XXX: configForm->maxResponseTime handled by subclass setFieldData(kIsOverrideChecksum, configForm->overrideChecksum->isChecked()); setFieldData(kChecksum, @@ -1077,8 +1066,8 @@ void GmpProtocol::storeConfigWidget() QVariantList grpList; for (int i = 0; i < configForm->groupList->count(); i++) { - grpList.append(configForm->groupList->item(i)->data(Qt::UserRole) - .toMap()); + QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); } setFieldData(kGroupRecords, grpList); diff --git a/common/gmp.h b/common/gmp.h index 1a9fc7e..f2af688 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -25,34 +25,35 @@ along with this program. If not, see #include "abstractprotocol.h" - enum GmpMsgType - { - kIgmpV1Query = 0x11, - kIgmpV1Report = 0x12, +#include - kIgmpV2Query = 0xFF11, - kIgmpV2Report = 0x16, - kIgmpV2Leave = 0x17, +// Both IGMP and MLD use the same msg type value for 'Query' message +// across versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional upper byte +enum GmpMsgType +{ + // IGMP + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, - kIgmpV3Query = 0xFE11, - kIgmpV3Report = 0x22, + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, - kMldV1Query = 0x82, - kMldV1Report = 0x83, - kMldV1Done = 0x84, + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, - kMldV2Query = 0xFF82, - kMldV2Report = 0x8F - }; + // MLD + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; /* -TODO:FIXME -Gmp Protocol Frame Format - - +-----+------+------+------+------+------+ - | A | B | LEN | CSUM | X | Y | - | (3) | (13) | (16) | (16) | (32) | (32) | - +-----+------+------+------+------+------+ -Figures in brackets represent field width in bits +Gmp Protocol Frame Format - TODO: for now see the respective RFCs */ class GmpProtocol; @@ -64,9 +65,16 @@ public: ~GmpConfigForm(); void update(); protected: + QString _defaultGroupIp; QString _defaultSourceIp; +private: + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; private slots: void on_msgTypeCombo_currentIndexChanged(int index); + void on_groupMode_currentIndexChanged(int index); void on_addSource_clicked(); void on_deleteSource_clicked(); @@ -109,24 +117,18 @@ protected: // ------------ // Frame Fields // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified kType = 0, kRsvdMrtCode, kChecksum, kMldMrt, // MLD Only (except MLDv2 Report) kMldRsvd, // MLD Only (except MLDv2 Report) - // Used ONLY in - - // IGMPv1: Query, Report - // IGMPv2: Report, Leave (v2 uses v1 Query only) - // IGMPv3: Query - // MLDv1: Query, Report, Done - // MLDv2: Query + // Field used in ASM messages kGroupAddress, FIELD_COUNT_ASM_ALL, - // Used ONLY in - - // IGMPv3: Query - // MLDv2: Query + // Fields used in SSM Query kRsvd1 = FIELD_COUNT_ASM_ALL, kSFlag, kQrv, @@ -135,9 +137,7 @@ protected: kSources, FIELD_COUNT_SSM_QUERY, - // Used ONLY in - - // IGMPv3: Report - // MLDv2: Report + // Fields used in SSM Report kRsvd2 = FIELD_COUNT_SSM_QUERY, kGroupRecordCount, kGroupRecords, @@ -163,30 +163,48 @@ protected: OstProto::Gmp data; GmpConfigForm *configForm; - GmpMsgType msgType() const - { - return GmpMsgType(fieldData(kType, FieldValue).toUInt()); - } - bool isSsmReport() const - { - return ((msgType() == kIgmpV3Report) - || (msgType() == kMldV2Report )); - } - bool isQuery() const - { - return ((msgType() == kIgmpV1Query) - || (msgType() == kIgmpV2Query) - || (msgType() == kIgmpV3Query) - || (msgType() == kMldV1Query ) - || (msgType() == kMldV2Query )); - } - bool isSsmQuery() const - { - return ((msgType() == kIgmpV3Query) - || (msgType() == kMldV2Query )); - } + GmpMsgType msgType() const; + + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + int qqic(int value) const; virtual quint16 checksum(int streamIndex) const = 0; +private: + static QHash frameFieldCountMap; }; +inline GmpMsgType GmpProtocol::msgType() const +{ + return GmpMsgType(fieldData(kType, FieldValue).toUInt()); +} + +inline bool GmpProtocol::isSsmReport() const +{ + return ((msgType() == kIgmpV3Report) + || (msgType() == kMldV2Report )); +} + +inline bool GmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query) + || (msgType() == kMldV1Query ) + || (msgType() == kMldV2Query )); +} + +inline bool GmpProtocol::isSsmQuery() const +{ + return ((msgType() == kIgmpV3Query) + || (msgType() == kMldV2Query )); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + #endif diff --git a/common/gmp.proto b/common/gmp.proto index 57e4eef..3d253d2 100755 --- a/common/gmp.proto +++ b/common/gmp.proto @@ -23,13 +23,14 @@ package OstProto; // Group Management Protocol (i.e. IGMP and MLD) message Gmp { - // TODO: field numbers and default values - - optional uint32 type = 1; // TODO: default value + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; optional bool is_override_rsvd_code = 2; - optional uint32 rsvd_code = 3 [default = 0]; + optional uint32 rsvd_code = 3; // MaxRespTime is in milliseconds - MaxRespCode will be derived - optional uint32 max_response_time = 4[default = 100]; + optional uint32 max_response_time = 4 [default = 100]; optional bool is_override_checksum = 5; optional uint32 checksum = 6; @@ -39,35 +40,34 @@ message Gmp { optional fixed64 v6_lo = 3; } - // used by - // IGMPv1: Query, Report - // IGMPv2: Report, Leave (v2 uses v1 Query only) - // MLDv1: Query, Report, Done + // + // Fields used in ASM messages + // enum GroupMode { kFixed = 0; kIncrementGroup = 1; kDecrementGroup = 2; kRandomGroup = 3; } - optional IpAddress group_address = 10; // TODO: default value + optional IpAddress group_address = 10; optional GroupMode group_mode = 11 [default = kFixed]; optional uint32 group_count = 12 [default = 16]; - optional uint32 group_prefix = 13 [default = 24]; // TODO: verify mcast ip range + optional uint32 group_prefix = 13 [default = 24]; - // used by - // IGMPv3: Query - // MLDv2: Query + // + // Fields used in SSM Query + // optional bool s_flag = 20; optional uint32 qrv = 21 [default = 2]; // QuerierQueryInterval is in seconds - QQIC will be derived - optional uint32 qqi = 22; + optional uint32 qqi = 22 [default = 125]; repeated IpAddress sources = 23; optional bool is_override_source_count = 24; optional uint32 source_count = 25; - // used by - // IGMPv3: Report - // MLDv2: Report + // + // Fields used in SSM Reports + // message GroupRecord { enum RecordType { kIsInclude = 1; @@ -79,7 +79,7 @@ message Gmp { } optional RecordType type = 1 [default = kIsInclude]; - optional IpAddress group_address = 2; // TODO: default + optional IpAddress group_address = 2; repeated IpAddress sources = 3; optional bool is_override_source_count = 4; optional uint32 source_count = 5; diff --git a/common/gmp.ui b/common/gmp.ui index 82e84f1..d19333e 100755 --- a/common/gmp.ui +++ b/common/gmp.ui @@ -5,7 +5,7 @@ 0 0 - 497 + 509 355 @@ -31,7 +31,7 @@ - Max Response Time + Max Response Time (1/10s) maxResponseTime @@ -155,6 +155,9 @@ + + false + 0 @@ -165,6 +168,9 @@ + + false + 0 @@ -304,7 +310,7 @@ - -- + – @@ -428,7 +434,7 @@ - -- + – @@ -580,7 +586,7 @@ - -- + – diff --git a/common/igmp.cpp b/common/igmp.cpp index 09a9feb..de8d06f 100644 --- a/common/igmp.cpp +++ b/common/igmp.cpp @@ -19,37 +19,22 @@ along with this program. If not, see #include "igmp.h" +#include "ipv4addressdelegate.h" +#include "iputils.h" + #include -#include #include -class IpAddressDelegate : public QItemDelegate -{ -public: - IpAddressDelegate(QObject *parent = 0) - : QItemDelegate(parent) { } - ~IpAddressDelegate() {} - QWidget* createEditor(QWidget *parent, - const QStyleOptionViewItem &option, const QModelIndex &index) const - { - QLineEdit *ipEdit; - - ipEdit = static_cast(QItemDelegate::createEditor( - parent, option, index)); - - ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator - - return ipEdit; - } -}; - IgmpConfigForm::IgmpConfigForm(QWidget *parent) : GmpConfigForm(parent) { + _defaultGroupIp = "0.0.0.0"; _defaultSourceIp = "0.0.0.0"; - sourceList->setItemDelegate(new IpAddressDelegate(this)); - groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); } IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) @@ -114,15 +99,19 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, { case kRsvdMrtCode: { - quint8 mrt = 0, mrc = 0; + uint mrt = 0; + quint8 mrcode = 0; if (msgType() == kIgmpV3Query) { mrt = data.max_response_time(); - mrc = mrt; // TODO: MR Code + mrcode = quint8(mrc(mrt)); } else if (msgType() == kIgmpV2Query) - mrc = mrt = data.max_response_time() & 0xFF; + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } switch(attrib) @@ -137,7 +126,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, case FieldTextValue: return QString("%1").arg(mrt); case FieldFrameValue: - return QByteArray(1, mrc); + return QByteArray(1, mrcode); default: break; } @@ -145,14 +134,12 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, } case kGroupAddress: { - quint32 grpIp = data.group_address().v4(); // FIXME -#if 0 // TODO - getip( - data.group_address().v4(), - data.group_mode(), - data.group_count(), - data.group_prefix()); -#endif + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); switch(attrib) { @@ -225,10 +212,10 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, grpRec["groupRecordAddress"] = QHostAddress( rec.group_address().v4()).toString(); - QStringList l; + QStringList sl; for (int j = 0; j < rec.sources_size(); j++) - l.append(QHostAddress(rec.sources(j).v4()).toString()); - grpRec["groupRecordSourceList"] = l; + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; grpRecords.replace(i, grpRec); } @@ -273,10 +260,10 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, QHostAddress(rec.group_address().v4()).toString())); str.append("; Sources: "); - QStringList l; + QStringList sl; for (int j = 0; j < rec.sources_size(); j++) - l.append(QHostAddress(rec.sources(j).v4()).toString()); - str.append(l.join(", ")); + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); recStr.replace("XXX", str); list.replace(i, recStr); @@ -305,6 +292,13 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value, switch (index) { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } case kGroupAddress: { QHostAddress addr(value.toString()); @@ -343,6 +337,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value, QStringList srcList = grpRec["groupRecordSourceList"] .toStringList(); + rec->clear_sources(); foreach (QString src, srcList) { rec->add_sources()->set_v4( @@ -380,48 +375,20 @@ void IgmpProtocol::loadConfigWidget() configForm->maxResponseTime->setText( fieldData(kRsvdMrtCode, FieldValue).toString()); -#if 0 - configForm->igmpA->setText(fieldData(igmp_a, FieldValue).toString()); - configForm->igmpB->setText(fieldData(igmp_b, FieldValue).toString()); - - configForm->igmpPayloadLength->setText( - fieldData(igmp_payloadLength, FieldValue).toString()); - - configForm->isChecksumOverride->setChecked( - fieldData(igmp_is_override_checksum, FieldValue).toBool()); - configForm->igmpChecksum->setText(uintToHexStr( - fieldData(igmp_checksum, FieldValue).toUInt(), 2)); - - configForm->igmpX->setText(fieldData(igmp_x, FieldValue).toString()); - configForm->igmpY->setText(fieldData(igmp_y, FieldValue).toString()); -#endif } void IgmpProtocol::storeConfigWidget() { - bool isOk; - GmpProtocol::storeConfigWidget(); -#if 0 - setFieldData(igmp_a, configForm->igmpA->text()); - setFieldData(igmp_b, configForm->igmpB->text()); - - setFieldData(igmp_payloadLength, configForm->igmpPayloadLength->text()); - setFieldData(igmp_is_override_checksum, - configForm->isChecksumOverride->isChecked()); - setFieldData(igmp_checksum, configForm->igmpChecksum->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(igmp_x, configForm->igmpX->text()); - setFieldData(igmp_y, configForm->igmpY->text()); -#endif + setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); } quint16 IgmpProtocol::checksum(int streamIndex) const { quint16 cks; quint32 sum = 0; -#if 0 // FIXME +#if 1 // FIXME // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto cks = protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cks; diff --git a/common/igmp.h b/common/igmp.h index c528fc8..a140a70 100644 --- a/common/igmp.h +++ b/common/igmp.h @@ -58,6 +58,13 @@ public: protected: virtual quint16 checksum(int streamIndex) const; +private: + int mrc(int value) const; }; +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + #endif diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..d135f41 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,70 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ostproto.pro b/common/ostproto.pro index 3fdbc39..c513a31 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -73,6 +73,7 @@ HEADERS += \ arp.h \ ip4.h \ ip6.h \ + ipv4addressdelegate.h \ ip6over4.h \ ip4over6.h \ ip4over4.h \ From a465e926a5c323b5b909fc5378e32c4fdbf13f61 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 6 Nov 2010 23:40:07 +0530 Subject: [PATCH 104/294] IGMPv3/MLDv2 record type +1/-1 jugglery removed --- common/gmp.cpp | 6 +++--- common/gmp.proto | 1 + common/gmp.ui | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index cddef4f..f477a4e 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -123,7 +123,7 @@ void GmpConfigForm::on_addGroupRecord_clicked() QVariantMap grpRec; QListWidgetItem *item = new QListWidgetItem; - grpRec["groupRecordType"] = defRec.type()-1; + grpRec["groupRecordType"] = defRec.type(); grpRec["groupRecordAddress"] = _defaultGroupIp; grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); grpRec["groupRecordSourceCount"] = defRec.source_count(); @@ -615,7 +615,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, QVariantMap grpRec; OstProto::Gmp::GroupRecord rec = data.group_records(i); - grpRec["groupRecordType"] = rec.type()-1; + grpRec["groupRecordType"] = rec.type(); // grpRec["groupRecordAddress"] = subclass responsibility grpRec["overrideGroupRecordSourceCount"] = rec.is_override_source_count(); @@ -877,7 +877,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value, OstProto::Gmp::GroupRecord *rec = data.add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( - grpRec["groupRecordType"].toInt() + 1)); + grpRec["groupRecordType"].toInt())); // NOTE: rec->group_address => subclass responsibility rec->set_is_override_source_count( grpRec["overrideGroupRecordSourceCount"].toBool()); diff --git a/common/gmp.proto b/common/gmp.proto index 3d253d2..f1fbf56 100755 --- a/common/gmp.proto +++ b/common/gmp.proto @@ -70,6 +70,7 @@ message Gmp { // message GroupRecord { enum RecordType { + kReserved = 0; kIsInclude = 1; kIsExclude = 2; kToInclude = 3; diff --git a/common/gmp.ui b/common/gmp.ui index d19333e..6260af6 100755 --- a/common/gmp.ui +++ b/common/gmp.ui @@ -502,6 +502,11 @@ + + + Reserved + + Is Include From 790e1cb383ecf89e61f1b467ecd4f678eac413ea Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 7 Nov 2010 12:51:22 +0530 Subject: [PATCH 105/294] IGMP/MLD msg types have been moved into the IGMP/MLD subclasses from GMP. Packet is now correctly padded with zero rather than garbage. Implemented AbstractProtocol::protocolHasPayload() method to force payload to None for IGMP/MLD/ARP etc. --- client/streamconfigdialog.cpp | 9 ++++++ common/abstractprotocol.cpp | 13 ++++++++ common/abstractprotocol.h | 5 +++ common/arp.cpp | 1 + common/gmp.cpp | 47 --------------------------- common/gmp.h | 60 ++++------------------------------- common/igmp.cpp | 50 +++++++++++++++++++++++++++-- common/igmp.h | 40 +++++++++++++++++++++++ common/mld.cpp | 33 +++++++++++++++++++ common/mld.h | 37 +++++++++++++++++++++ common/protocolmanager.cpp | 14 ++++++-- common/protocolmanager.h | 3 +- common/streambase.cpp | 4 +++ 13 files changed, 209 insertions(+), 107 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 913e773..e04983e 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -113,6 +113,15 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, if (validProtocolCount == 0) connect(btn1, SIGNAL(clicked(bool)), bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } } } } diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 5462b19..ef80783 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -79,6 +79,7 @@ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) _metaFieldCount = -1; _frameFieldCount = -1; protoSize = -1; + _hasPayload = true; } /*! @@ -614,6 +615,18 @@ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const return false; } +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + /*! Returns the checksum (of the requested type) of the protocol's contents diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 053e303..07350cc 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -59,6 +59,9 @@ protected: AbstractProtocol *prev; //!< Protocol preceding this protocol AbstractProtocol *next; //!< Protocol succeeding this protocol + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + public: //! Properties of a field, can be OR'd enum FieldFlag { @@ -139,6 +142,8 @@ public: bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; + bool protocolHasPayload() const; + virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, diff --git a/common/arp.cpp b/common/arp.cpp index 844a2d7..f23b6b6 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -88,6 +88,7 @@ void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { + _hasPayload = false; configForm = NULL; } diff --git a/common/gmp.cpp b/common/gmp.cpp index f477a4e..7bfb938 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -29,16 +29,6 @@ GmpConfigForm::GmpConfigForm(QWidget *parent) { setupUi(this); - // TODO: this should be in subclass - msgTypeCombo->setValueMask(0xFF); - msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); - msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); - msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); - msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); - msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); - msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); - msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); - auxData->setValidator(new QRegExpValidator( QRegExp("[0-9A-Fa-f]*"), this)); } @@ -54,43 +44,6 @@ void GmpConfigForm::update() groupList->currentItem()); } -void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) -{ - switch(msgTypeCombo->currentValue()) - { - case kIgmpV1Query: - case kIgmpV1Report: - case kIgmpV2Query: - case kIgmpV2Report: - case kIgmpV2Leave: - case kMldV1Query: - case kMldV1Report: - case kMldV1Done: - asmGroup->show(); - ssmWidget->hide(); - break; - - case kIgmpV3Query: - case kMldV2Query: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmQueryPage); - ssmWidget->show(); - break; - - case kIgmpV3Report: - case kMldV2Report: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmReportPage); - ssmWidget->show(); - break; - - default: - asmGroup->hide(); - ssmWidget->hide(); - break; - } -} - void GmpConfigForm::on_groupMode_currentIndexChanged(int index) { bool disabled = (index == 0); diff --git a/common/gmp.h b/common/gmp.h index f2af688..97dc5fd 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -27,31 +27,6 @@ along with this program. If not, see #include -// Both IGMP and MLD use the same msg type value for 'Query' message -// across versions despite the fields being different. To distinguish -// Query messages of different versions, we use an additional upper byte -enum GmpMsgType -{ - // IGMP - kIgmpV1Query = 0x11, - kIgmpV1Report = 0x12, - - kIgmpV2Query = 0xFF11, - kIgmpV2Report = 0x16, - kIgmpV2Leave = 0x17, - - kIgmpV3Query = 0xFE11, - kIgmpV3Report = 0x22, - - // MLD - kMldV1Query = 0x82, - kMldV1Report = 0x83, - kMldV1Done = 0x84, - - kMldV2Query = 0xFF82, - kMldV2Report = 0x8F -}; - /* Gmp Protocol Frame Format - TODO: for now see the respective RFCs */ @@ -67,13 +42,11 @@ public: protected: QString _defaultGroupIp; QString _defaultSourceIp; -private: enum { kSsmQueryPage = 0, kSsmReportPage = 1 }; private slots: - void on_msgTypeCombo_currentIndexChanged(int index); void on_groupMode_currentIndexChanged(int index); void on_addSource_clicked(); void on_deleteSource_clicked(); @@ -163,11 +136,11 @@ protected: OstProto::Gmp data; GmpConfigForm *configForm; - GmpMsgType msgType() const; + int msgType() const; - virtual bool isSsmReport() const; - virtual bool isQuery() const; - virtual bool isSsmQuery() const; + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; int qqic(int value) const; @@ -176,30 +149,9 @@ private: static QHash frameFieldCountMap; }; -inline GmpMsgType GmpProtocol::msgType() const +inline int GmpProtocol::msgType() const { - return GmpMsgType(fieldData(kType, FieldValue).toUInt()); -} - -inline bool GmpProtocol::isSsmReport() const -{ - return ((msgType() == kIgmpV3Report) - || (msgType() == kMldV2Report )); -} - -inline bool GmpProtocol::isQuery() const -{ - return ((msgType() == kIgmpV1Query) - || (msgType() == kIgmpV2Query) - || (msgType() == kIgmpV3Query) - || (msgType() == kMldV1Query ) - || (msgType() == kMldV2Query )); -} - -inline bool GmpProtocol::isSsmQuery() const -{ - return ((msgType() == kIgmpV3Query) - || (msgType() == kMldV2Query )); + return fieldData(kType, FieldValue).toInt(); } inline int GmpProtocol::qqic(int value) const diff --git a/common/igmp.cpp b/common/igmp.cpp index de8d06f..046f675 100644 --- a/common/igmp.cpp +++ b/common/igmp.cpp @@ -28,6 +28,18 @@ along with this program. If not, see IgmpConfigForm::IgmpConfigForm(QWidget *parent) : GmpConfigForm(parent) { + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + _defaultGroupIp = "0.0.0.0"; _defaultSourceIp = "0.0.0.0"; @@ -37,9 +49,43 @@ IgmpConfigForm::IgmpConfigForm(QWidget *parent) groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); } +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) { + _hasPayload = false; + data.set_type(kIgmpV2Query); } @@ -388,7 +434,7 @@ quint16 IgmpProtocol::checksum(int streamIndex) const { quint16 cks; quint32 sum = 0; -#if 1 // FIXME + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto cks = protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cks; @@ -398,6 +444,6 @@ quint16 IgmpProtocol::checksum(int streamIndex) const sum = (sum & 0xFFFF) + (sum >> 16); cks = (~sum) & 0xFFFF; -#endif + return cks; } diff --git a/common/igmp.h b/common/igmp.h index a140a70..a5d50d4 100644 --- a/common/igmp.h +++ b/common/igmp.h @@ -22,11 +22,30 @@ along with this program. If not, see #include "igmp.pb.h" #include "gmp.h" +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + class IgmpConfigForm : public GmpConfigForm { + Q_OBJECT public: IgmpConfigForm(QWidget *parent = 0); private slots: + void on_msgTypeCombo_currentIndexChanged(int index); }; class IgmpProtocol : public GmpProtocol @@ -57,11 +76,32 @@ public: virtual void storeConfigWidget(); protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + virtual quint16 checksum(int streamIndex) const; private: int mrc(int value) const; }; +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + inline int IgmpProtocol::mrc(int value) const { return quint8(value); // TODO: if value > 128, convert to mantissa/exp form diff --git a/common/mld.cpp b/common/mld.cpp index 2f50535..7caf77a 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -49,11 +49,44 @@ public: MldConfigForm::MldConfigForm(QWidget *parent) : GmpConfigForm(parent) { + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + _defaultSourceIp = "::"; sourceList->setItemDelegate(new IpAddressDelegate(this)); groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); } +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) { diff --git a/common/mld.h b/common/mld.h index 0d087c9..a66965f 100644 --- a/common/mld.h +++ b/common/mld.h @@ -25,11 +25,27 @@ along with this program. If not, see #include "abstractprotocol.h" +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + class MldConfigForm : public GmpConfigForm { + Q_OBJECT public: MldConfigForm(QWidget *parent = 0); private slots: + void on_msgTypeCombo_currentIndexChanged(int index); }; class MldProtocol : public GmpProtocol @@ -60,7 +76,28 @@ public: virtual void loadConfigWidget(); virtual void storeConfigWidget(); protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + virtual quint16 checksum(int streamIndex) const; }; +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + + #endif diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index abcc518..24f5e87 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -122,8 +122,9 @@ ProtocolManager::~ProtocolManager() nameToNumberMap.clear(); neighbourProtocols.clear(); factory.clear(); - while (!protocolList.isEmpty()) - delete protocolList.takeFirst(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); } void ProtocolManager::registerProtocol(int protoNumber, @@ -136,7 +137,7 @@ void ProtocolManager::registerProtocol(int protoNumber, factory.insert(protoNumber, protoInstanceCreator); p = createProtocol(protoNumber, NULL); - protocolList.append(p); + protocolList.insert(protoNumber, p); numberToNameMap.insert(protoNumber, p->shortName()); nameToNumberMap.insert(p->shortName(), protoNumber); @@ -190,6 +191,13 @@ bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) return false; } +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + QStringList ProtocolManager::protocolDatabase() { return numberToNameMap.values(); diff --git a/common/protocolmanager.h b/common/protocolmanager.h index ac64919..77ea81c 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -32,7 +32,7 @@ class ProtocolManager QMap nameToNumberMap; QMultiMap neighbourProtocols; QMap factory; - QList protocolList; + QMap protocolList; void populateNeighbourProtocols(); @@ -48,6 +48,7 @@ public: AbstractProtocol *parent = 0); bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); QStringList protocolDatabase(); }; diff --git a/common/streambase.cpp b/common/streambase.cpp index 6ba1909..f766fd7 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -444,6 +444,10 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const } delete iter; + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + return pktLen; } From 1d1e6479c384ddb75c689a51fa9f635ca2d488c1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 7 Nov 2010 18:10:09 +0530 Subject: [PATCH 106/294] MLD review and rework --- common/igmp.h | 1 + common/iputils.h | 53 ++++- common/ipv6addressdelegate.h | 60 ++++++ common/mld.cpp | 361 +++++++++++++++++++++++++++-------- common/mld.h | 11 +- common/ostproto.pro | 1 + 6 files changed, 407 insertions(+), 80 deletions(-) create mode 100644 common/ipv6addressdelegate.h diff --git a/common/igmp.h b/common/igmp.h index a5d50d4..6ee9ad3 100644 --- a/common/igmp.h +++ b/common/igmp.h @@ -81,6 +81,7 @@ protected: virtual bool isSsmQuery() const; virtual quint16 checksum(int streamIndex) const; + private: int mrc(int value) const; }; diff --git a/common/iputils.h b/common/iputils.h index d135f41..466880f 100644 --- a/common/iputils.h +++ b/common/iputils.h @@ -28,7 +28,7 @@ enum AddrMode { kRandom = 3 }; -quint32 ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, int index) { int u; @@ -66,5 +66,56 @@ quint32 ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, return ip; } +void inline ipAddress(quint64 baseIpHi, quint64 baseIpLo, int prefix, + AddrMode mode, int count, int index, quint64 &ipHi, quint64 &ipLo) +{ + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi, hostLo; + + switch(mode) + { + case kFixed: + ipHi = baseIpHi; + ipLo = baseIpLo; + break; + case kIncrement: + case kDecrement: + case kRandom: + u = index % count; + if (prefix > 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + u) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - u) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + } // namespace ipUtils #endif diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/mld.cpp b/common/mld.cpp index 7caf77a..9427642 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -19,42 +19,33 @@ along with this program. If not, see #include "mld.h" +#include "ipv6addressdelegate.h" #include "ipv6addressvalidator.h" +#include "iputils.h" #include -#include #include -class IpAddressDelegate : public QItemDelegate -{ -public: - IpAddressDelegate(QObject *parent = 0) - : QItemDelegate(parent) { } - ~IpAddressDelegate() {} - QWidget* createEditor(QWidget *parent, - const QStyleOptionViewItem &option, const QModelIndex &index) const - { - QLineEdit *ipEdit; - - ipEdit = static_cast(QItemDelegate::createEditor( - parent, option, index)); - - // FIXME: const problem!!! - //ipEdit->setValidator(new IPv6AddressValidator(this)); - - return ipEdit; - } -}; - MldConfigForm::MldConfigForm(QWidget *parent) : GmpConfigForm(parent) { connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), SLOT(on_msgTypeCombo_currentIndexChanged(int))); + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; _defaultSourceIp = "::"; - sourceList->setItemDelegate(new IpAddressDelegate(this)); - groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); } void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) @@ -90,6 +81,8 @@ void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) { + _hasPayload = false; + data.set_type(kMldV1Query); } @@ -179,15 +172,15 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, case kMldMrt: { - quint16 mrt = 0, mrc; + quint16 mrt = 0, mrcode = 0; if (msgType() == kMldV2Query) { mrt = data.max_response_time(); - mrc = mrt; // TODO: MR Code + mrcode = mrc(mrt); } - if (msgType() == kMldV1Query) - mrc = mrt = data.max_response_time() & 0xFFFF; + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; switch(attrib) { @@ -204,7 +197,32 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, QByteArray fv; fv.resize(2); - qToBigEndian(mrc, (uchar*) fv.data()); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); return fv; } default: @@ -215,24 +233,24 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, case kGroupAddress: { quint64 grpHi, grpLo; -#if 0 - AbstractProtocol::getip( + + ipUtils::ipAddress( data.group_address().v6_hi(), data.group_address().v6_lo(), - data.group_mode(), - data.group_count(), data.group_prefix(), - &grpHi, - &grpLo); -#endif + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); switch(attrib) { case FieldName: return QString("Group Address"); case FieldValue: - case FieldFrameValue: case FieldTextValue: + case FieldFrameValue: { QByteArray fv; fv.resize(16); @@ -250,22 +268,36 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, } case kSources: { - quint64 grpHi, grpLo; - switch(attrib) { case FieldName: return QString("Source List"); case FieldValue: - return QVariant(); // FIXME + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.group_address().v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.group_address().v6_hi(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } case FieldFrameValue: { QByteArray fv; fv.resize(16 * data.sources_size()); for (int i = 0; i < data.sources_size(); i++) { - qToBigEndian(grpHi, (uchar*)(fv.data() + i*16)); - qToBigEndian(grpLo, (uchar*)(fv.data() + i*16 + 8)); + qToBigEndian(data.group_address().v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.group_address().v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); } return fv; } @@ -276,8 +308,10 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, fv.resize(16); for (int i = 0; i < data.sources_size(); i++) { - qToBigEndian(grpHi, (uchar*)fv.data()); - qToBigEndian(grpLo, (uchar*)fv.data()+8); + qToBigEndian(data.group_address().v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.group_address().v6_hi(), + (uchar*)fv.data()+8); list << QHostAddress((quint8*)fv.constData()).toString(); } @@ -289,8 +323,117 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, break; } case kGroupRecords: - // TODO + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress(ip.constData()) + .toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress(ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+12+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+12+16*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress(ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress(ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } break; + } default: break; } @@ -336,12 +479,106 @@ bool MldProtocol::setFieldData(int index, const QVariant &value, } case kSources: - //TODO + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } break; + } case kGroupRecords: - //TODO + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + break; + } default: isOk = GmpProtocol::setFieldData(index, value, attrib); @@ -370,41 +607,13 @@ void MldProtocol::loadConfigWidget() configForm->maxResponseTime->setText( fieldData(kMldMrt, FieldValue).toString()); -#if 0 - configForm->mldA->setText(fieldData(mld_a, FieldValue).toString()); - configForm->mldB->setText(fieldData(mld_b, FieldValue).toString()); - - configForm->mldPayloadLength->setText( - fieldData(mld_payloadLength, FieldValue).toString()); - - configForm->isChecksumOverride->setChecked( - fieldData(mld_is_override_checksum, FieldValue).toBool()); - configForm->mldChecksum->setText(uintToHexStr( - fieldData(mld_checksum, FieldValue).toUInt(), 2)); - - configForm->mldX->setText(fieldData(mld_x, FieldValue).toString()); - configForm->mldY->setText(fieldData(mld_y, FieldValue).toString()); -#endif } void MldProtocol::storeConfigWidget() { - bool isOk; - GmpProtocol::storeConfigWidget(); -#if 0 - setFieldData(mld_a, configForm->mldA->text()); - setFieldData(mld_b, configForm->mldB->text()); - - setFieldData(mld_payloadLength, configForm->mldPayloadLength->text()); - setFieldData(mld_is_override_checksum, - configForm->isChecksumOverride->isChecked()); - setFieldData(mld_checksum, configForm->mldChecksum->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(mld_x, configForm->mldX->text()); - setFieldData(mld_y, configForm->mldY->text()); -#endif + setFieldData(kMldMrt, configForm->maxResponseTime->text()); } quint16 MldProtocol::checksum(int streamIndex) const diff --git a/common/mld.h b/common/mld.h index a66965f..bc2d95e 100644 --- a/common/mld.h +++ b/common/mld.h @@ -16,15 +16,12 @@ 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 */ - #ifndef _MLD_H #define _MLD_H #include "mld.pb.h" #include "gmp.h" -#include "abstractprotocol.h" - // MLD uses the same msg type value for 'Query' messages across // versions despite the fields being different. To distinguish // Query messages of different versions, we use an additional @@ -75,12 +72,16 @@ public: virtual QWidget* configWidget(); virtual void loadConfigWidget(); virtual void storeConfigWidget(); + protected: virtual bool isSsmReport() const; virtual bool isQuery() const; virtual bool isSsmQuery() const; virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; }; inline bool MldProtocol::isSsmReport() const @@ -99,5 +100,9 @@ inline bool MldProtocol::isSsmQuery() const return (msgType() == kMldV2Query); } +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} #endif diff --git a/common/ostproto.pro b/common/ostproto.pro index c513a31..39bfa5c 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -74,6 +74,7 @@ HEADERS += \ ip4.h \ ip6.h \ ipv4addressdelegate.h \ + ipv6addressdelegate.h \ ip6over4.h \ ip4over6.h \ ip4over4.h \ From bea8fb736e9b24c64dd7175f4ad2ed17af767329 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 7 Nov 2010 20:05:24 +0530 Subject: [PATCH 107/294] MLD fixes --- common/gmp.cpp | 9 +++++++-- common/iputils.h | 4 ++-- common/mld.cpp | 34 ++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index 7bfb938..e19b54c 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -949,9 +949,14 @@ void GmpProtocol::loadConfigWidget() configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + QStringList sl = fieldData(kSources, FieldValue).toStringList(); configForm->sourceList->clear(); - configForm->sourceList->addItems( - fieldData(kSources, FieldValue).toStringList()); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + configForm->sourceList->addItem(item); + } // NOTE: SourceCount should be loaded after sourceList configForm->overrideSourceCount->setChecked( diff --git a/common/iputils.h b/common/iputils.h index 466880f..73ff488 100644 --- a/common/iputils.h +++ b/common/iputils.h @@ -98,11 +98,11 @@ void inline ipAddress(quint64 baseIpHi, quint64 baseIpLo, int prefix, prefixHi = baseIpHi & maskHi; prefixLo = baseIpLo & maskLo; if (mode == kIncrement) { - hostHi = ((baseIpHi & ~maskHi) + u) & ~maskHi; + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; } else if (mode == kDecrement) { - hostHi = ((baseIpHi & ~maskHi) - u) & ~maskHi; + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; } else if (mode==kRandom) { diff --git a/common/mld.cpp b/common/mld.cpp index 9427642..11a9649 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -279,9 +279,9 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, fv.resize(16); for (int i = 0; i < data.sources_size(); i++) { - qToBigEndian(data.group_address().v6_hi(), + qToBigEndian(data.sources(i).v6_hi(), (uchar*)fv.data()); - qToBigEndian(data.group_address().v6_hi(), + qToBigEndian(data.sources(i).v6_lo(), (uchar*)fv.data()+8); list << QHostAddress((quint8*)fv.constData()).toString(); @@ -294,9 +294,9 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, fv.resize(16 * data.sources_size()); for (int i = 0; i < data.sources_size(); i++) { - qToBigEndian(data.group_address().v6_hi(), + qToBigEndian(data.sources(i).v6_hi(), (uchar*)(fv.data() + i*16)); - qToBigEndian(data.group_address().v6_lo(), + qToBigEndian(data.sources(i).v6_lo(), (uchar*)(fv.data() + i*16 + 8)); } return fv; @@ -308,9 +308,9 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, fv.resize(16); for (int i = 0; i < data.sources_size(); i++) { - qToBigEndian(data.group_address().v6_hi(), + qToBigEndian(data.sources(i).v6_hi(), (uchar*)fv.data()); - qToBigEndian(data.group_address().v6_hi(), + qToBigEndian(data.sources(i).v6_lo(), (uchar*)fv.data()+8); list << QHostAddress((quint8*)fv.constData()).toString(); @@ -339,12 +339,12 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, QVariantMap grpRec = grpRecords.at(i).toMap(); OstProto::Gmp::GroupRecord rec = data.group_records(i); - qToBigEndian(rec.group_address().v6_hi(), + qToBigEndian(quint64(rec.group_address().v6_hi()), (uchar*)(ip.data())); - qToBigEndian(rec.group_address().v6_lo(), + qToBigEndian(quint64(rec.group_address().v6_lo()), (uchar*)(ip.data() + 8)); - grpRec["groupRecordAddress"] = QHostAddress(ip.constData()) - .toString(); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); QStringList sl; for (int j = 0; j < rec.sources_size(); j++) @@ -353,7 +353,8 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, (uchar*)(ip.data())); qToBigEndian(rec.sources(j).v6_lo(), (uchar*)(ip.data() + 8)); - sl.append(QHostAddress(ip.constData()).toString()); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); } grpRec["groupRecordSourceList"] = sl; @@ -377,14 +378,14 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); qToBigEndian(rec.group_address().v6_hi(), (uchar*)(rv.data()+4)); - qToBigEndian(rec.group_address().v6_hi(), + qToBigEndian(rec.group_address().v6_lo(), (uchar*)(rv.data()+4+8)); for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(rec.sources(j).v6_hi(), - (uchar*)(rv.data()+12+16*j)); + (uchar*)(rv.data()+20+16*j)); qToBigEndian(rec.sources(j).v6_lo(), - (uchar*)(rv.data()+12+16*j)); + (uchar*)(rv.data()+20+16*j)); } fv.append(rv); @@ -410,7 +411,7 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, qToBigEndian(rec.group_address().v6_lo(), (uchar*)(ip.data() + 8)); str.append(QString("Group: %1").arg( - QHostAddress(ip.constData()).toString())); + QHostAddress((quint8*)ip.constData()).toString())); str.append("; Sources: "); QStringList sl; @@ -420,7 +421,8 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, (uchar*)(ip.data())); qToBigEndian(rec.sources(j).v6_lo(), (uchar*)(ip.data() + 8)); - sl.append(QHostAddress(ip.constData()).toString()); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); } str.append(sl.join(", ")); From 3fb011cae4eea4cf48e7e21facb060e1c74ba68b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 8 Nov 2010 14:21:41 +0530 Subject: [PATCH 108/294] Fixed warnings --- common/gmp.cpp | 2 +- common/ip6.cpp | 4 ++-- common/iputils.h | 3 ++- common/mld.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index e19b54c..7bc7ced 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -192,7 +192,7 @@ void GmpConfigForm::on_auxData_textChanged(const QString &text) { // auxDataLength is in units of words and each byte is 2 chars in text() if (!overrideAuxDataLength->isChecked()) - auxDataLength->setText(QString().setNum((auxData->text().size()+7)/8)); + auxDataLength->setText(QString().setNum((text.size()+7)/8)); } GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) diff --git a/common/ip6.cpp b/common/ip6.cpp index f8b7555..fe5d29b 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -359,7 +359,7 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib, int u, p, q; quint64 maskHi = 0, maskLo = 0; quint64 prefixHi, prefixLo; - quint64 hostHi, hostLo; + quint64 hostHi = 0, hostLo = 0; quint64 srcHi = 0, srcLo = 0; switch(data.src_addr_mode()) @@ -433,7 +433,7 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib, int u, p, q; quint64 maskHi = 0, maskLo = 0; quint64 prefixHi, prefixLo; - quint64 hostHi, hostLo; + quint64 hostHi = 0, hostLo = 0; quint64 dstHi = 0, dstLo = 0; switch(data.dst_addr_mode()) diff --git a/common/iputils.h b/common/iputils.h index 73ff488..0d6a067 100644 --- a/common/iputils.h +++ b/common/iputils.h @@ -60,6 +60,7 @@ quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, ip = subnet | host; break; default: + ip = 0; qWarning("Unhandled mode = %d", mode); } @@ -72,7 +73,7 @@ void inline ipAddress(quint64 baseIpHi, quint64 baseIpLo, int prefix, int u, p, q; quint64 maskHi = 0, maskLo = 0; quint64 prefixHi, prefixLo; - quint64 hostHi, hostLo; + quint64 hostHi = 0, hostLo = 0; switch(mode) { diff --git a/common/mld.cpp b/common/mld.cpp index 11a9649..98feab5 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -232,7 +232,7 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, } case kGroupAddress: { - quint64 grpHi, grpLo; + quint64 grpHi = 0, grpLo = 0; ipUtils::ipAddress( data.group_address().v6_hi(), From fd2c0c73d1d62946245d3b278b4c6c6946c150ec Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 8 Nov 2010 16:34:14 +0530 Subject: [PATCH 109/294] Bumped the file format version number because of the new protocols added - HexDump, IGMP and MLD --- common/fileformat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/fileformat.h b/common/fileformat.h index acb8337..6fc8b07 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -49,7 +49,7 @@ private: // Native file format version static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 1; - static const uint kFileFormatVersionRevision = 1; + static const uint kFileFormatVersionRevision = 2; void initFileMetaData(OstProto::FileMetaData &metaData); }; From b5909e5e413d7530b082a20880f0e169aa39720b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 8 Nov 2010 16:34:47 +0530 Subject: [PATCH 110/294] Version bump to 0.3 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index c370b32..7422c3c 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.2 +APP_VERSION = 0.3 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 7b673a0d57c1e6604088d1d5642945c386facf4b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 25 Dec 2010 12:02:14 +0530 Subject: [PATCH 111/294] Implemented auto-reconnect for portgroups --- client/mainwindow.cpp | 2 -- client/portgroup.cpp | 32 ++++++++++++++++++++++++++++++++ client/portgroup.h | 13 ++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 291865f..623624f 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -56,8 +56,6 @@ MainWindow::MainWindow(QWidget *parent) localServer_ = new QProcess(this); localServer_->setProcessChannelMode(QProcess::ForwardedChannels); localServer_->start(serverApp, QStringList()); - // TODO: waitForReadyRead() is a kludge till we implement auto-retry! - localServer_->waitForReadyRead(1000); pgl = new PortGroupList; diff --git a/client/portgroup.cpp b/client/portgroup.cpp index faa1f0a..43e68a3 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -27,6 +27,7 @@ along with this program. If not, see #include #include #include +#include #include using ::google::protobuf::NewCallback; @@ -46,6 +47,13 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) statsController = new PbRpcController(portIdList_, portStatsList_); isGetStatsPending_ = false; + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + rpcChannel = new PbRpcChannel(ip, port); serviceStub = new OstProto::OstService::Stub(rpcChannel); @@ -75,6 +83,15 @@ PortGroup::~PortGroup() // ------------------------------------------------ // Slots // ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) { qDebug("state changed %d", state); @@ -98,6 +115,8 @@ void PortGroup::on_rpcChannel_connected() qDebug("connected\n"); emit portGroupDataChanged(mPortGroupId); + reconnectAfter = kMinReconnectWaitTime; + qDebug("requesting portlist ..."); PbRpcController *controller = new PbRpcController(void_, portIdList); @@ -115,12 +134,25 @@ void PortGroup::on_rpcChannel_disconnected() emit portListChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } } void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { qDebug("%s: error %d", __FUNCTION__, socketError); emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } } void PortGroup::processPortIdList(PbRpcController *controller) diff --git a/client/portgroup.h b/client/portgroup.h index dbf831c..8261576 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -37,6 +37,7 @@ LOW #define DEFAULT_SERVER_PORT 7878 class QFile; +class QTimer; class PortGroup : public QObject { Q_OBJECT @@ -46,6 +47,11 @@ private: quint32 mPortGroupId; QString mUserAlias; // user defined + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; PbRpcChannel *rpcChannel; PbRpcController *statsController; bool isGetStatsPending_; @@ -63,10 +69,10 @@ public: quint16 port = DEFAULT_SERVER_PORT); ~PortGroup(); - void connectToHost() { rpcChannel->establish(); } + void connectToHost() { reconnect = true; rpcChannel->establish(); } void connectToHost(QHostAddress ip, quint16 port) - { rpcChannel->establish(ip, port); } - void disconnectFromHost() { rpcChannel->tearDown(); } + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } int numPorts() const { return mPorts.size(); } quint32 id() const { return mPortGroupId; } @@ -123,6 +129,7 @@ signals: void statsChanged(quint32 portGroupId); private slots: + void on_reconnectTimer_timeout(); void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); void on_rpcChannel_connected(); void on_rpcChannel_disconnected(); From 71a8140abfcc6d53e65295653bed4f9fdc44ef9b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 27 Dec 2010 16:20:30 +0530 Subject: [PATCH 112/294] Provide descriptive text and further pointer to user if a port group has no ports --- client/portgroup.cpp | 27 +++++++++++++++++++++++++-- client/portgroup.h | 2 ++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 43e68a3..92bc097 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -67,6 +67,9 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) this, SLOT(on_rpcChannel_disconnected())); connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); } PortGroup::~PortGroup() @@ -155,6 +158,23 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) } } +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("Ostinato"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://ostinato.googlecode.com/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + void PortGroup::processPortIdList(PbRpcController *controller) { OstProto::PortIdList *portIdList @@ -221,7 +241,7 @@ void PortGroup::processPortConfigList(PbRpcController *controller) goto _error_exit; } - emit portListAboutToBeChanged(mPortGroupId); + //emit portListAboutToBeChanged(mPortGroupId); for(int i = 0; i < portConfigList->port_size(); i++) { @@ -232,7 +252,7 @@ void PortGroup::processPortConfigList(PbRpcController *controller) mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); } - emit portListChanged(mPortGroupId); + //emit portListChanged(mPortGroupId); // FIXME: check if we need new signals since we are not changing the // number of ports, just the port data @@ -735,6 +755,9 @@ void PortGroup::getPortStats() if (state() != QAbstractSocket::ConnectedState) goto _exit; + if (numPorts() <= 0) + goto _exit; + if (isGetStatsPending_) goto _exit; diff --git a/client/portgroup.h b/client/portgroup.h index 8261576..254e731 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -135,6 +135,8 @@ private slots: void on_rpcChannel_disconnected(); void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + void when_portListChanged(quint32 portGroupId); + public slots: void when_configApply(int portIndex); From 8d85fd30defa458c42cdf70ded6c130eb9804fc5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 17 Jan 2011 18:38:45 +0530 Subject: [PATCH 113/294] Jumbo frames support added. Max packet size currently is set to 16K - underlying OS and hardware needs to support jumbo frames to be able to actually use this feature. Fixes issue 27 --- client/portgroup.cpp | 2 +- client/streamconfigdialog.cpp | 5 +++-- client/streamconfigdialog.h | 4 ++-- client/streamconfigdialog.ui | 27 --------------------------- common/streambase.cpp | 14 ++++++++++---- server/abstractport.cpp | 9 ++++----- server/abstractport.h | 4 ++++ server/pcapport.cpp | 2 ++ 8 files changed, 26 insertions(+), 41 deletions(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 92bc097..63bc968 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -162,7 +162,7 @@ void PortGroup::when_portListChanged(quint32 /*portGroupId*/) { if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) { - QMessageBox::warning(NULL, tr("Ostinato"), + QMessageBox::warning(NULL, tr("No ports in portgroup"), QString("The portgroup %1:%2 does not contain any ports!\n\n" "Packet Transmit/Capture requires elevated privileges. " "Please ensure that you are running 'drone' - the server " diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 94b5ded..981fabc 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -257,8 +257,9 @@ void StreamConfigDialog::setupUiExtra() ** Setup Validators */ // Meta Data - //! \todo - doesn't seem to work - range validator needs a spinbox? - //lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); /* ** Setup Connections diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 5709570..bb91ce7 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -27,9 +27,9 @@ along with this program. If not, see #include "packetmodel.h" #include "modeltest.h" -#define MAX_MAC_ITER_COUNT 256 +#define MAX_MAC_ITER_COUNT 256 #define MIN_PKT_LEN 64 -#define MAX_PKT_LEN 1522 +#define MAX_PKT_LEN 16384 /* ** TODO diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 6ec20a3..1f580a3 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -104,15 +104,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff false - - 0099; - - - - - - 4 - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -120,15 +111,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - - - - - 32767 - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -146,15 +128,6 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff false - - 0099; - - - - - - 4 - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/common/streambase.cpp b/common/streambase.cpp index f766fd7..53addbe 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -453,6 +453,7 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const bool StreamBase::preflightCheck(QString &result) const { + bool pass = true; int count = isFrameSizeVariable() ? frameCount() : 1; for (int i = 0; i < count; i++) @@ -462,13 +463,18 @@ bool StreamBase::preflightCheck(QString &result) const result += QString("One or more frames may be truncated - " "frame length should be at least %1.\n") .arg(frameProtocolLength(i) + kFcsSize); - goto _fail; + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; } } - return true; -_fail: - return false; + return pass; } bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 5a08911..d0e8c0c 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -113,7 +113,6 @@ void AbstractPort::updatePacketList() { int len; bool isVariable; - uchar pktBuf[2000]; long sec = 0; long usec = 0; @@ -162,7 +161,7 @@ void AbstractPort::updatePacketList() else { isVariable = false; - len = streamList_[i]->frameValue(pktBuf, sizeof(pktBuf), 0); + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), 0); } for (int j = 0; j < numBursts; j++) @@ -171,8 +170,8 @@ void AbstractPort::updatePacketList() { if (isVariable) { - len = streamList_[i]->frameValue(pktBuf, - sizeof(pktBuf), j * numPackets + k); + len = streamList_[i]->frameValue(pktBuf_, + sizeof(pktBuf_), j * numPackets + k); } if (len <= 0) continue; @@ -180,7 +179,7 @@ void AbstractPort::updatePacketList() qDebug("q(%d, %d, %d) sec = %lu usec = %lu", i, j, k, sec, usec); - appendToPacketList(sec, usec, pktBuf, len); + appendToPacketList(sec, usec, pktBuf_, len); usec += ipg; if (usec > 1000000) diff --git a/server/abstractport.h b/server/abstractport.h index 4b036fa..e903007 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -94,6 +94,10 @@ protected: private: bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ QList streamList_; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 222dd42..d4d1223 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -315,6 +315,8 @@ void PcapPort::PortTransmitter::run() int i; qDebug("sendQueueList_.size = %d", sendQueueList_.size()); + if (sendQueueList_.size() <= 0) + return; for(i = 0; i < sendQueueList_.size(); i++) { From f140717f81148079920bdf8c19e66a63f143b68f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 30 Jan 2011 15:31:50 +0530 Subject: [PATCH 114/294] Following protocols now allow overriding of their type/length/protocolId fields - Ethernet II, 802.3, LLC, SNAP, IPv4. Since new fields have been added to the .proto files, the Ostinato native file format revision number has been bumped Fixes Issue 20 --- common/dot3.cpp | 98 +++++++++++++++++++++++----- common/dot3.h | 4 ++ common/dot3.proto | 3 +- common/dot3.ui | 47 +++++++++++--- common/eth2.cpp | 67 +++++++++++++++++-- common/eth2.h | 5 +- common/eth2.proto | 4 +- common/eth2.ui | 28 ++++++-- common/fileformat.h | 2 +- common/ip4.cpp | 15 ++++- common/ip4.h | 1 + common/ip4.proto | 3 +- common/ip4.ui | 79 ++++++++++++++++++----- common/llc.cpp | 154 +++++++++++++++++++++++++++++++++++++++++--- common/llc.h | 8 ++- common/llc.proto | 10 ++- common/llc.ui | 91 ++++++++++++++++++++------ common/snap.cpp | 144 ++++++++++++++++++++++++++++++++++++----- common/snap.h | 7 +- common/snap.proto | 7 +- common/snap.ui | 73 +++++++++++++++++++-- 21 files changed, 732 insertions(+), 118 deletions(-) diff --git a/common/dot3.cpp b/common/dot3.cpp index e97bdc0..ea5f6a5 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -17,17 +17,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "dot3.h" #include "streambase.h" +#include +#include +#include Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) : QWidget(parent) { setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); } Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) @@ -80,6 +81,29 @@ int Dot3Protocol::fieldCount() const return dot3_fieldCount; } +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -92,24 +116,23 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, return QString("Length"); case FieldValue: { - quint16 len; - - len = protocolFramePayloadSize(streamIndex); + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); return len; } case FieldTextValue: { - quint16 len; + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); - len = protocolFramePayloadSize(streamIndex); return QString("%1").arg(len); } case FieldFrameValue: { - quint16 len; + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); QByteArray fv; - len = protocolFramePayloadSize(streamIndex); fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; @@ -121,17 +144,58 @@ QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, } break; + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool Dot3Protocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; } bool Dot3Protocol::isProtocolFrameValueVariable() const @@ -153,16 +217,18 @@ void Dot3Protocol::loadConfigWidget() { configWidget(); + configForm->cbOverrideLength->setChecked( + fieldData(dot3_is_override_length, FieldValue).toBool()); configForm->leLength->setText( fieldData(dot3_length, FieldValue).toString()); } void Dot3Protocol::storeConfigWidget() { - bool isOk; - configWidget(); - data.set_length(configForm->leLength->text().toULong(&isOk)); + setFieldData(dot3_is_override_length, + configForm->cbOverrideLength->isChecked()); + setFieldData(dot3_length,configForm->leLength->text()); } diff --git a/common/dot3.h b/common/dot3.h index 8a0d5c2..403a2ce 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -41,6 +41,9 @@ private: { dot3_length, + // Meta-fields + dot3_is_override_length, + dot3_fieldCount }; @@ -60,6 +63,7 @@ public: virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/dot3.proto b/common/dot3.proto index 2e4070c..f20f120 100644 --- a/common/dot3.proto +++ b/common/dot3.proto @@ -23,7 +23,8 @@ package OstProto; // 802.3 message Dot3 { - optional uint32 length = 1; + optional bool is_override_length = 2; + optional uint32 length = 1; } extend Protocol { diff --git a/common/dot3.ui b/common/dot3.ui index 7473ee2..5631eaf 100644 --- a/common/dot3.ui +++ b/common/dot3.ui @@ -5,28 +5,25 @@ 0 0 - 131 - 72 + 181 + 98 Form - - + + 802.3 - + Length - - leLength - @@ -39,7 +36,7 @@ - + Qt::Horizontal @@ -52,8 +49,38 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + - + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp index dd85063..73038b7 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -83,6 +83,29 @@ int Eth2Protocol::fieldCount() const return eth2_fieldCount; } +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -90,21 +113,27 @@ QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, { case eth2_type: { - quint16 type; switch(attrib) { case FieldName: return QString("Type"); case FieldValue: - type = payloadProtocolId(ProtocolIdEth); + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); return type; + } case FieldTextValue: - type = payloadProtocolId(ProtocolIdEth); + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } case FieldFrameValue: { QByteArray fv; - type = payloadProtocolId(ProtocolIdEth); + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); fv.resize(2); qToBigEndian((quint16) type, (uchar*) fv.data()); return fv; @@ -114,7 +143,23 @@ QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, } break; } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } @@ -136,8 +181,18 @@ bool Eth2Protocol::setFieldData(int index, const QVariant &value, uint type = value.toUInt(&isOk); if (isOk) data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; } default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return isOk; @@ -157,6 +212,8 @@ void Eth2Protocol::loadConfigWidget() { configWidget(); + configForm->cbOverrideType->setChecked( + fieldData(eth2_is_override_type, FieldValue).toBool()); configForm->leType->setText(uintToHexStr( fieldData(eth2_type, FieldValue).toUInt(), 2)); } @@ -167,6 +224,8 @@ void Eth2Protocol::storeConfigWidget() configWidget(); + setFieldData(eth2_is_override_type, + configForm->cbOverrideType->isChecked()); data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); } diff --git a/common/eth2.h b/common/eth2.h index dcb8a7a..5694339 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -41,6 +41,8 @@ private: { eth2_type = 0, + eth2_is_override_type, + eth2_fieldCount }; @@ -60,8 +62,9 @@ public: virtual ProtocolIdType protocolIdType() const; - virtual int fieldCount() const; + virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/eth2.proto b/common/eth2.proto index 72d58c8..47db7e7 100644 --- a/common/eth2.proto +++ b/common/eth2.proto @@ -23,7 +23,9 @@ package OstProto; // Ethernet II message Eth2 { - optional uint32 type = 1; + optional bool is_override_type = 2; + + optional uint32 type = 1; } extend Protocol { diff --git a/common/eth2.ui b/common/eth2.ui index 7bf6cdf..a43fb36 100644 --- a/common/eth2.ui +++ b/common/eth2.ui @@ -5,7 +5,7 @@ 0 0 - 267 + 190 64 @@ -14,13 +14,10 @@ - + Ethernet Type - - leType - @@ -46,7 +43,7 @@ - + Qt::Vertical @@ -62,5 +59,22 @@ - + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + diff --git a/common/fileformat.h b/common/fileformat.h index 6fc8b07..ebdc97c 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -49,7 +49,7 @@ private: // Native file format version static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 1; - static const uint kFileFormatVersionRevision = 2; + static const uint kFileFormatVersionRevision = 3; void initFileMetaData(OstProto::FileMetaData &metaData); }; diff --git a/common/ip4.cpp b/common/ip4.cpp index 776752f..4cc789b 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -166,6 +166,7 @@ AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const case ip4_isOverrideVer: case ip4_isOverrideHdrLen: case ip4_isOverrideTotLen: + case ip4_isOverrideProto: case ip4_isOverrideCksum: case ip4_srcAddrMode: case ip4_srcAddrCount: @@ -381,17 +382,20 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, return QString("Protocol"); case FieldValue: { - unsigned char id = payloadProtocolId(ProtocolIdIp); + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); return id; } case FieldFrameValue: { - unsigned char id = payloadProtocolId(ProtocolIdIp); + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); return QByteArray(1, (char) id); } case FieldTextValue: { - unsigned char id = payloadProtocolId(ProtocolIdIp); + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); return QString("0x%1"). arg(id, 2, BASE_HEX, QChar('0')); } @@ -559,6 +563,7 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, case ip4_isOverrideVer: case ip4_isOverrideHdrLen: case ip4_isOverrideTotLen: + case ip4_isOverrideProto: case ip4_isOverrideCksum: case ip4_srcAddrMode: @@ -669,6 +674,8 @@ void Ip4Protocol::loadConfigWidget() configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); configForm->leIpTtl->setText(QString().setNum(data.ttl())); + + configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); configForm->leIpProto->setText(uintToHexStr( fieldData(ip4_proto, FieldValue).toUInt(), 1)); @@ -712,6 +719,8 @@ void Ip4Protocol::storeConfigWidget() data.set_flags(ff); data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + + data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); diff --git a/common/ip4.h b/common/ip4.h index ab7b3de..006d9ca 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -64,6 +64,7 @@ private: ip4_isOverrideVer, ip4_isOverrideHdrLen, ip4_isOverrideTotLen, + ip4_isOverrideProto, ip4_isOverrideCksum, ip4_srcAddrMode, diff --git a/common/ip4.proto b/common/ip4.proto index 64407ad..be7391d 100644 --- a/common/ip4.proto +++ b/common/ip4.proto @@ -33,7 +33,8 @@ message Ip4 { optional bool is_override_ver = 1; optional bool is_override_hdrlen = 2; optional bool is_override_totlen = 3; - optional bool is_override_cksum = 4; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; optional uint32 ver_hdrlen = 5 [default = 0x45]; optional uint32 tos = 6; diff --git a/common/ip4.ui b/common/ip4.ui index c457a2d..3e98d7c 100644 --- a/common/ip4.ui +++ b/common/ip4.ui @@ -5,7 +5,7 @@ 0 0 - 493 + 507 308 @@ -137,18 +137,14 @@ Length (x4)
- - - - Protocol - - - false + + >HH; + @@ -171,6 +167,13 @@ Length (x4) + + + + Override Protocol + + + @@ -399,6 +402,34 @@ Length (x4) + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + @@ -424,8 +455,8 @@ Length (x4) setEnabled(bool) - 118 - 43 + 113 + 67 166 @@ -440,12 +471,12 @@ Length (x4) setEnabled(bool) - 79 - 97 + 89 + 118 - 172 - 97 + 236 + 119 @@ -456,8 +487,8 @@ Length (x4) setEnabled(bool) - 345 - 122 + 387 + 140 406 @@ -465,5 +496,21 @@ Length (x4) + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + diff --git a/common/llc.cpp b/common/llc.cpp index 76e8e46..5adecf1 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -83,6 +83,33 @@ int LlcProtocol::fieldCount() const return llc_fieldCount; } +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -90,9 +117,9 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, quint8 dsap, ssap, ctl; id = payloadProtocolId(ProtocolIdLlc); - dsap = (id >> 16) & 0xFF; - ssap = (id >> 8) & 0xFF; - ctl = (id >> 0) & 0xFF; + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; switch (index) { @@ -142,17 +169,109 @@ QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, } break; + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool LlcProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; } @@ -173,10 +292,18 @@ void LlcProtocol::loadConfigWidget() configWidget(); + configForm->cbOverrideDsap->setChecked( + fieldData(llc_is_override_dsap, FieldValue).toBool()); configForm->leDsap->setText(uintToHexStr( fieldData(llc_dsap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideSsap->setChecked( + fieldData(llc_is_override_ssap, FieldValue).toBool()); configForm->leSsap->setText(uintToHexStr( fieldData(llc_ssap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideControl->setChecked( + fieldData(llc_is_override_ctl, FieldValue).toBool()); configForm->leControl->setText(uintToHexStr( fieldData(llc_ctl, FieldValue).toUInt(), 1)); #undef uintToHexStr @@ -188,8 +315,17 @@ void LlcProtocol::storeConfigWidget() configWidget(); - data.set_dsap(configForm->leDsap->text().toULong(&isOk, BASE_HEX)); - data.set_ssap(configForm->leSsap->text().toULong(&isOk, BASE_HEX)); - data.set_ctl(configForm->leControl->text().toULong(&isOk, BASE_HEX)); + setFieldData(llc_is_override_dsap, + configForm->cbOverrideDsap->isChecked()); + setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ssap, + configForm->cbOverrideSsap->isChecked()); + setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ctl, + configForm->cbOverrideControl->isChecked()); + setFieldData(llc_ctl, + configForm->leControl->text().toUInt(&isOk, BASE_HEX)); } diff --git a/common/llc.h b/common/llc.h index 2cc9fe8..808d442 100644 --- a/common/llc.h +++ b/common/llc.h @@ -46,6 +46,11 @@ private: llc_ssap, llc_ctl, + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + llc_fieldCount }; @@ -65,8 +70,9 @@ public: virtual ProtocolIdType protocolIdType() const; - virtual int fieldCount() const; + virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/llc.proto b/common/llc.proto index e0681aa..360b935 100644 --- a/common/llc.proto +++ b/common/llc.proto @@ -22,9 +22,13 @@ import "protocol.proto"; package OstProto; message Llc { - optional uint32 dsap = 1; - optional uint32 ssap = 2; - optional uint32 ctl = 3; + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; } extend Protocol { diff --git a/common/llc.ui b/common/llc.ui index 5f305a0..e61f54e 100644 --- a/common/llc.ui +++ b/common/llc.ui @@ -5,8 +5,8 @@ 0 0 - 304 - 72 + 396 + 98 @@ -18,21 +18,18 @@ Form - - + + LLC - + DSAP - - leDsap - @@ -46,13 +43,10 @@ - + SSAP - - leSsap - @@ -66,13 +60,10 @@ - + Control - - leControl - @@ -88,21 +79,83 @@ - + Qt::Horizontal - 40 + 20 20 + + + + Qt::Vertical + + + + 20 + 40 + + + + - + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + diff --git a/common/snap.cpp b/common/snap.cpp index 6803253..bdb80c3 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -22,6 +22,8 @@ along with this program. If not, see #include "snap.h" +quint32 kStdOui = 0x000000; + SnapConfigForm::SnapConfigForm(QWidget *parent) : QWidget(parent) { @@ -89,11 +91,36 @@ quint32 SnapProtocol::protocolId(ProtocolIdType type) const return AbstractProtocol::protocolId(type); } -int SnapProtocol::fieldCount() const +int SnapProtocol::fieldCount() const { return snap_fieldCount; } +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { @@ -105,14 +132,21 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("OUI"); case FieldValue: - return data.oui(); + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } case FieldTextValue: - return QString("%1").arg(data.oui(), 6, BASE_HEX, QChar('0')); + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } case FieldFrameValue: { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; QByteArray fv; fv.resize(4); - qToBigEndian((quint32) data.oui(), (uchar*) fv.data()); + qToBigEndian(oui, (uchar*) fv.data()); fv.remove(0, 1); return fv; } @@ -129,16 +163,19 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, case FieldName: return QString("Type"); case FieldValue: - type = payloadProtocolId(ProtocolIdEth); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); return type; case FieldTextValue: - type = payloadProtocolId(ProtocolIdEth); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); - type = payloadProtocolId(ProtocolIdEth); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); qToBigEndian(type, (uchar*) fv.data()); return fv; } @@ -147,19 +184,86 @@ QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, } break; } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool SnapProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; -} + bool isOk = false; + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} QWidget* SnapProtocol::configWidget() { @@ -175,8 +279,13 @@ void SnapProtocol::loadConfigWidget() { configWidget(); + configForm->cbOverrideOui->setChecked( + fieldData(snap_is_override_oui, FieldValue).toBool()); configForm->leOui->setText(uintToHexStr( fieldData(snap_oui, FieldValue).toUInt(), 3)); + + configForm->cbOverrideType->setChecked( + fieldData(snap_is_override_type, FieldValue).toBool()); configForm->leType->setText(uintToHexStr( fieldData(snap_type, FieldValue).toUInt(), 2)); } @@ -184,10 +293,15 @@ void SnapProtocol::loadConfigWidget() void SnapProtocol::storeConfigWidget() { bool isOk; - configWidget(); - data.set_oui(configForm->leOui->text().toULong(&isOk, BASE_HEX)); - data.set_type(configForm->leType->text().toULong(&isOk, BASE_HEX)); -} + setFieldData(snap_is_override_oui, + configForm->cbOverrideOui->isChecked()); + setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(snap_is_override_type, + configForm->cbOverrideType->isChecked()); + setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); +} diff --git a/common/snap.h b/common/snap.h index 2e60b84..daba802 100644 --- a/common/snap.h +++ b/common/snap.h @@ -42,6 +42,10 @@ private: snap_oui = 0, snap_type, + // Meta fields + snap_is_override_oui, + snap_is_override_type, + snap_fieldCount }; @@ -62,8 +66,9 @@ public: virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; - virtual int fieldCount() const; + virtual int fieldCount() const; + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, diff --git a/common/snap.proto b/common/snap.proto index 1354100..26c607c 100644 --- a/common/snap.proto +++ b/common/snap.proto @@ -22,8 +22,11 @@ import "protocol.proto"; package OstProto; message Snap { - optional uint32 oui = 1; - optional uint32 type = 2; + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; } extend Protocol { diff --git a/common/snap.ui b/common/snap.ui index 80997bd..374c7a8 100644 --- a/common/snap.ui +++ b/common/snap.ui @@ -5,22 +5,22 @@ 0 0 - 194 - 72 + 268 + 98 Form - - + + SNAP - + OUI @@ -37,7 +37,7 @@ - + Type @@ -56,8 +56,67 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + - + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + From 481005a994c445fd2b1c96d7d504790886b76318 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 30 Jan 2011 16:38:37 +0530 Subject: [PATCH 115/294] Fixed assert (crash) when opening a stream file saved by a newer version of Ostinato containing a new/unknown protocol --- common/protocolmanager.cpp | 5 +++++ common/protocolmanager.h | 1 + common/streambase.cpp | 10 ++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 9ed67f5..e91f270 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -164,6 +164,11 @@ void ProtocolManager::populateNeighbourProtocols() } } +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, StreamBase *stream, AbstractProtocol *parent) { diff --git a/common/protocolmanager.h b/common/protocolmanager.h index 77ea81c..07b0604 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -42,6 +42,7 @@ public: void registerProtocol(int protoNumber, void *protoInstanceCreator); + bool isRegisteredProtocol(int protoNumber); AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, AbstractProtocol *parent = 0); AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, diff --git a/common/streambase.cpp b/common/streambase.cpp index 53addbe..b9dea25 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -89,8 +89,14 @@ void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) iter = createProtocolListIterator(); for (int i=0; i < stream.protocol_size(); i++) { - proto = OstProtocolManager->createProtocol( - stream.protocol(i).protocol_id().id(), this); + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); proto->protoDataCopyFrom(stream.protocol(i)); iter->insert(proto); } From a9dbb7bdd27d0edb688d2e8550973ab0731fe6cf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 30 Jan 2011 17:38:03 +0530 Subject: [PATCH 116/294] pdml initial working version --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 ++++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 63 ++ client/mainwindow.cpp | 116 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 ++++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 85 ++ client/ostinato.qrc | 37 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 266 +++++ client/port.h | 130 +++ client/portgroup.cpp | 837 +++++++++++++++ client/portgroup.h | 145 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 338 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 ++++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 315 ++++++ client/portstatsmodel.h | 141 +++ client/portstatswindow.cpp | 180 ++++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 ++++ client/portswindow.cpp | 518 ++++++++++ client/portswindow.h | 80 ++ client/portswindow.ui | 248 +++++ client/preferences.cpp | 56 + client/preferences.h | 41 + client/preferences.ui | 114 +++ client/settings.h | 41 + client/stream.cpp | 79 ++ client/stream.h | 42 + client/streamconfigdialog.cpp | 1005 ++++++++++++++++++ client/streamconfigdialog.h | 135 +++ client/streamconfigdialog.ui | 1353 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 283 ++++++ client/streammodel.h | 78 ++ common/abstractprotocol.cpp | 825 +++++++++++++++ common/abstractprotocol.h | 162 +++ common/arp.cpp | 954 +++++++++++++++++ common/arp.h | 120 +++ common/arp.proto | 67 ++ common/arp.ui | 518 ++++++++++ common/comboprotocol.h | 217 ++++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot3.cpp | 234 +++++ common/dot3.h | 79 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/eth2.cpp | 231 +++++ common/eth2.h | 78 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/fileformat.cpp | 411 ++++++++ common/fileformat.h | 59 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 1036 +++++++++++++++++++ common/gmp.h | 162 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 +++++++++++++++ common/hexdump.cpp | 263 +++++ common/hexdump.h | 89 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/icmp.cpp | 594 +++++++++++ common/icmp.h | 117 +++ common/icmp.proto | 44 + common/icmp.ui | 178 ++++ common/igmp.cpp | 449 ++++++++ common/igmp.h | 111 ++ common/igmp.proto | 27 + common/intcombobox.h | 69 ++ common/ip4.cpp | 739 ++++++++++++++ common/ip4.h | 115 +++ common/ip4.proto | 66 ++ common/ip4.ui | 516 ++++++++++ common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip6.cpp | 940 +++++++++++++++++ common/ip6.h | 130 +++ common/ip6.proto | 61 ++ common/ip6.ui | 467 +++++++++ common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/iputils.h | 122 +++ common/ipv4addressdelegate.h | 58 ++ common/ipv6addressdelegate.h | 60 ++ common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 331 ++++++ common/llc.h | 86 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/mac.cpp | 317 ++++++ common/mac.h | 90 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/mld.cpp | 624 ++++++++++++ common/mld.h | 108 ++ common/mld.proto | 27 + common/ostproto.pro | 131 +++ common/payload.cpp | 258 +++++ common/payload.h | 84 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/pdml_p.cpp | 497 +++++++++ common/pdml_p.h | 132 +++ common/pdmlfileformat.cpp | 70 ++ common/pdmlfileformat.h | 44 + common/protocol.proto | 235 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 212 ++++ common/protocolmanager.h | 57 ++ common/sample.cpp | 534 ++++++++++ common/sample.h | 102 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/snap.cpp | 307 ++++++ common/snap.h | 82 ++ common/snap.proto | 34 + common/snap.ui | 122 +++ common/streambase.cpp | 489 +++++++++ common/streambase.h | 144 +++ common/svlan.cpp | 68 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/tcp.cpp | 699 +++++++++++++ common/tcp.h | 100 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/textproto.cpp | 295 ++++++ common/textproto.h | 91 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/udp.cpp | 492 +++++++++ common/udp.h | 87 ++ common/udp.proto | 39 + common/udp.ui | 174 ++++ common/userscript.cpp | 609 +++++++++++ common/userscript.h | 181 ++++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/vlan.cpp | 257 +++++ common/vlan.h | 82 ++ common/vlan.proto | 34 + common/vlan.ui | 179 ++++ common/vlanstack.h | 30 + common/vlanstack.proto | 31 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 8 + extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 106 ++ extra/qhexedit2/src/qhexedit.h | 145 +++ extra/qhexedit2/src/qhexedit_p.cpp | 414 ++++++++ extra/qhexedit2/src/qhexedit_p.h | 82 ++ install.pri | 14 + ost.pro | 8 + protobuf.pri | 33 + rpc/pbhelper.h | 170 ++++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 338 ++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 39 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 291 ++++++ rpc/rpcserver.h | 66 ++ server/abstractport.cpp | 249 +++++ server/abstractport.h | 108 ++ server/drone.cpp | 102 ++ server/drone.h | 56 + server/drone.pro | 45 + server/drone.qrc | 5 + server/drone.ui | 190 ++++ server/drone_main.cpp | 49 + server/icons/portgroup.png | Bin 0 -> 667 bytes server/myservice.cpp | 470 +++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 505 +++++++++ server/pcapport.h | 149 +++ server/portmanager.cpp | 77 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 215 ++++ server/winpcapport.h | 56 + version.pri | 19 + 249 files changed, 38146 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/comboprotocol.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/ostproto.pro create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/pdml_p.cpp create mode 100644 common/pdml_p.h create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone.qrc create mode 100644 server/drone.ui create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..34cd66c --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2010 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/protocolmanager.h" + +#include +#include +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; +QMainWindow *mainWindow; + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + delete mainWindow; + delete appSettings; + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..623624f --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,116 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + statsDock = new QDockWidget(tr("Statistics"), this); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..c12c781 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,85 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..bd88fd1 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,37 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..c1e402c --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,266 @@ +/* +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 +*/ + +#include "port.h" + +#include "fileformat.h" +#include "pdmlfileformat.h" + +#include +#include +#include +#include + +uint Port::mAllocStreamId = 0; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + OstProto::StreamConfigList streams; + + //if (!fileFormat.openStreams(fileName, streams, error)) + if (!pdmlFileFormat.openStreams(fileName, streams, error)) + goto _fail; + + if (!append) + { + while (numStreams()) + deleteStreamAt(0); + } + + for (int i = 0; i < streams.stream_size(); i++) + { + newStreamAt(mStreams.size(), &streams.stream(i)); + } + + emit streamListChanged(mPortGroupId, mPortId); + + return true; + +_fail: + return false; +} + +bool Port::saveStreams(QString fileName, QString &error) +{ + OstProto::StreamConfigList streams; + + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + + return fileFormat.saveStreams(streams, fileName, error); +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..2fbb9a0 --- /dev/null +++ b/client/port.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString &error); + +signals: + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..63bc968 --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,837 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://ostinato.googlecode.com/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, bool isExclusive) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + port->set_is_exclusive_control(isExclusive); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portGroupDataChanged(mPortGroupId); + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..254e731 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,145 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { reconnect = true; rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, bool isExclusive); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..1d13eda --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..92e6a24 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,315 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..d50508b --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,141 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..58070a0 --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,518 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include +#include +#include + +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& /*previous*/) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + updatePortViewActions(tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + plm->portGroup(current.parent()).modifyPort(current.row(), checked); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + bool append = true; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams")); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?")); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + if (!plm->port(current).openStreams(fileName, append, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + QString fileName; + QString errorStr; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams")); + if (fileName.isEmpty()) + goto _exit; + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..ed93854 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a724c06 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,248 @@ + + PortsWindow + + + + 0 + 0 + 689 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Capacity + + + + + + + + + + Aggr fps + + + + + + + + + + % age + + + + + + + + + + Aggr bps + + + + + + + + + + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..4f372ee --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,56 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..1821346 --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..514ed42 --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,114 @@ + + Preferences + + + + 0 + 0 + 400 + 164 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + Wireshark Path + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..3fbf4cf --- /dev/null +++ b/client/settings.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..a24c971 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,79 @@ +/* +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 +*/ + +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..981fabc --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1005 @@ +/* +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 +*/ + +#include + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager *OstProtocolManager; + +int StreamConfigDialog::lastTopLevelTabIndex = 0; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setDisabled(true); + pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + leStreamId->setDisabled(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); + + // Finally, restore the saved last selected tab for the various tab widgets + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + QWidget *selWidget; + + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + selWidget = tbProtocolData->currentWidget(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + tbProtocolData->setCurrentWidget(selWidget); + + tbProtocolData->show(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if (!mpStream->preflightCheck(log)) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastTopLevelTabIndex = twTopLevel->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..bb91ce7 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,135 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time + static int lastTopLevelTabIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..1f580a3 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1353 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto Stream + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + Qt::Horizontal + + + + 41 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + leNumPackets + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bursts/Sec + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Gaps + + + false + + + false + + + + + + + + + icons/gaps.png + + + + + + + ISG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leNumPackets + + + + + + + IBG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
dumpview.h
+ 1 +
+
+ + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 558 + 453 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 169 + 207 + + + 169 + 207 + + + + + rbSendPackets + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 125 + 207 + + + 156 + 207 + + + + +
diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..19942fd --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,283 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + return (int) StreamMaxFields; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..9c7e1aa --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..ef80783 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,825 @@ +/* +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 +*/ + +#include + +#include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..07350cc --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,162 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..f23b6b6 --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,954 @@ +/* +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 +*/ + +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + return true; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..677b73a --- /dev/null +++ b/common/arp.h @@ -0,0 +1,120 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..29cf71d --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..ea5f6a5 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,234 @@ +/* +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 +*/ + +#include "dot3.h" +#include "streambase.h" + +#include +#include +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Dot3Protocol::~Dot3Protocol() +{ + delete configForm; +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +QWidget* Dot3Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideLength->setChecked( + fieldData(dot3_is_override_length, FieldValue).toBool()); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); +} + +void Dot3Protocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(dot3_is_override_length, + configForm->cbOverrideLength->isChecked()); + setFieldData(dot3_length,configForm->leLength->text()); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..403a2ce --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,79 @@ +/* +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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..73038b7 --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,231 @@ +/* +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 +*/ + +#include +#include + +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Eth2Protocol::~Eth2Protocol() +{ + delete configForm; +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideType->setChecked( + fieldData(eth2_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(eth2_is_override_type, + configForm->cbOverrideType->isChecked()); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5694339 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + +public: + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..619ab37 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,411 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + Q_ASSERT(meta.data().format_version_minor() == kFileFormatVersionMinor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..ebdc97c --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,59 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "fileformat.pb.h" + +#include +#include + +class FileFormat : public QObject +{ + Q_OBJECT +public: + FileFormat(); + ~FileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + +private: + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 1; + static const uint kFileFormatVersionRevision = 3; + + void initFileMetaData(OstProto::FileMetaData &metaData); +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..7bc7ced --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,1036 @@ +/* +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 +*/ + +#include "gmp.h" + +#include +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +GmpProtocol::~GmpProtocol() +{ + delete configForm; +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +void GmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); + // XXX: configForm->maxResponseTime set by subclass + configForm->overrideChecksum->setChecked( + fieldData(kIsOverrideChecksum, FieldValue).toBool()); + configForm->checksum->setText(uintToHexStr( + fieldData(kChecksum, FieldValue).toUInt(), 2)); + + configForm->groupAddress->setText( + fieldData(kGroupAddress, FieldValue).toString()); + configForm->groupMode->setCurrentIndex( + fieldData(kGroupMode, FieldValue).toUInt()); + configForm->groupCount->setText( + fieldData(kGroupCount, FieldValue).toString()); + configForm->groupPrefix->setText( + fieldData(kGroupPrefix, FieldValue).toString()); + + configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); + configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); + configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + + QStringList sl = fieldData(kSources, FieldValue).toStringList(); + configForm->sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + configForm->sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + configForm->overrideSourceCount->setChecked( + fieldData(kIsOverrideSourceCount, FieldValue).toBool()); + configForm->sourceCount->setText( + fieldData(kSourceCount, FieldValue).toString()); + + QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); + configForm->groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(configForm->groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + configForm->groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + configForm->overrideGroupRecordCount->setChecked( + fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); + configForm->groupRecordCount->setText( + fieldData(kGroupRecordCount, FieldValue).toString()); + +} + +void GmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + configForm->update(); + + setFieldData(kType, configForm->msgTypeCombo->currentValue()); + // XXX: configForm->maxResponseTime handled by subclass + setFieldData(kIsOverrideChecksum, + configForm->overrideChecksum->isChecked()); + setFieldData(kChecksum, + configForm->checksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kGroupAddress, configForm->groupAddress->text()); + setFieldData(kGroupMode, configForm->groupMode->currentIndex()); + setFieldData(kGroupCount, configForm->groupCount->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); + + setFieldData(kSFlag, configForm->sFlag->isChecked()); + setFieldData(kQrv, configForm->qrv->text()); + setFieldData(kQqic, configForm->qqi->text()); + + QStringList list; + for (int i = 0; i < configForm->sourceList->count(); i++) + list.append(configForm->sourceList->item(i)->text()); + setFieldData(kSources, list); + + // sourceCount should be AFTER sources + setFieldData(kIsOverrideSourceCount, + configForm->overrideSourceCount->isChecked()); + setFieldData(kSourceCount, configForm->sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < configForm->groupList->count(); i++) + { + QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + setFieldData(kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + setFieldData(kIsOverrideGroupRecordCount, + configForm->overrideGroupRecordCount->isChecked()); + setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..97dc5fd --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,162 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "gmp.pb.h" +#include "ui_gmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ +class GmpProtocol; + +class GmpConfigForm : public QWidget, public Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + void update(); +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +class GmpProtocol : public AbstractProtocol +{ +public: + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + OstProto::Gmp data; + GmpConfigForm *configForm; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..f579430 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,263 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +#include + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +HexDumpProtocol::~HexDumpProtocol() +{ + delete configForm; +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + +QWidget* HexDumpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new HexDumpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void HexDumpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hexEdit->setData( + fieldData(hexDump_content, FieldValue).toByteArray()); + configForm->padUntilEnd->setChecked( + fieldData(hexDump_pad_until_end, FieldValue).toBool()); +} + +void HexDumpProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(hexDump_content, configForm->hexEdit->data()); + setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..f5b6932 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "hexdump.pb.h" +#include "ui_hexdump.h" + +#include "abstractprotocol.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpConfigForm : public QWidget, public Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +class HexDumpProtocol : public AbstractProtocol +{ +public: + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + OstProto::HexDump data; + HexDumpConfigForm *configForm; + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
qhexedit.h
+ 1 +
+
+ + +
diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..e7f6267 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,594 @@ +/* +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 +*/ + + +#include "icmp.h" + +#include +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->versionGroup->button(icmpVersion())->click(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..a3fc296 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,117 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + QButtonGroup *versionGroup; + + IcmpConfigForm(QWidget *parent = 0); +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..046f675 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,449 @@ +/* +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 +*/ + +#include "igmp.h" + +#include "ipv4addressdelegate.h" +#include "iputils.h" + +#include +#include + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* IgmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new IgmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IgmpProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kRsvdMrtCode, FieldValue).toString()); +} + +void IgmpProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..6ee9ad3 --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,111 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "igmp.pb.h" +#include "gmp.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..4cc789b --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,739 @@ +/* +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 +*/ + +#include +#include + +#include "ip4.h" + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Ip4Protocol::~Ip4Protocol() +{ + delete configForm; +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip4Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + + configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); + + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + configWidget(); + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + + data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..006d9ca --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,115 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..fe5d29b --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,940 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..1856bc8 --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..5adecf1 --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,331 @@ +/* +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 +*/ + +#include +#include + +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +LlcProtocol::~LlcProtocol() +{ + delete configForm; +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + + +QWidget* LlcProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configWidget(); + + configForm->cbOverrideDsap->setChecked( + fieldData(llc_is_override_dsap, FieldValue).toBool()); + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideSsap->setChecked( + fieldData(llc_is_override_ssap, FieldValue).toBool()); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideControl->setChecked( + fieldData(llc_is_override_ctl, FieldValue).toBool()); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(llc_is_override_dsap, + configForm->cbOverrideDsap->isChecked()); + setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ssap, + configForm->cbOverrideSsap->isChecked()); + setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ctl, + configForm->cbOverrideControl->isChecked()); + setFieldData(llc_ctl, + configForm->leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..808d442 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..040f870 --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,317 @@ +/* +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 +*/ + +#include +#include + +#include "mac.h" + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +MacProtocol::~MacProtocol() +{ + delete configForm; +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + + +QWidget* MacProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..48d0e35 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,90 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..98feab5 --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,624 @@ +/* +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 +*/ + +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" +#include "iputils.h" + +#include +#include + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* MldProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new MldConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void MldProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kMldMrt, FieldValue).toString()); +} + +void MldProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kMldMrt, configForm->maxResponseTime->text()); +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..bc2d95e --- /dev/null +++ b/common/mld.h @@ -0,0 +1,108 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "mld.pb.h" +#include "gmp.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..26aefae --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,131 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network script xml +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf +FORMS += \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + hexdump.ui \ + sample.ui +PROTOS += \ + protocol.proto \ + fileformat.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto +HEADERS += \ + abstractprotocol.h \ + comboprotocol.h \ + fileformat.h \ + pdmlfileformat.h \ + pdml_p.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + arp.h \ + ip4.h \ + ip6.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + ip6over4.h \ + ip4over6.h \ + ip4over4.h \ + ip6over6.h \ + icmp.h \ + gmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + userscript.h \ + hexdump.h \ + sample.h +SOURCES += \ + abstractprotocol.cpp \ + crc32c.cpp \ + fileformat.cpp \ + pdmlfileformat.cpp \ + pdml_p.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + icmp.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + userscript.cpp \ + hexdump.cpp \ + sample.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..d5fb2e5 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,258 @@ +/* +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 +*/ + +#include +#include + +//#include "../client/stream.h" +#include "payload.h" +#include "streambase.h" + + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +PayloadProtocol::~PayloadProtocol() +{ + delete configForm; +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + + +QWidget* PayloadProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void PayloadProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..2fd32d2 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,84 @@ +/* +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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp new file mode 100644 index 0000000..4d8eee7 --- /dev/null +++ b/common/pdml_p.cpp @@ -0,0 +1,497 @@ +/* +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 +*/ + +#include "pdml_p.h" + +#include "mac.pb.h" +#include "eth2.pb.h" +#include "ip4.pb.h" +#include "hexdump.pb.h" + +#include + +#include + +#include + +const int kBaseHex = 16; + +PdmlDefaultProtocol::PdmlDefaultProtocol() +{ + ostProtoId_ = -1; +} + +PdmlDefaultProtocol::~PdmlDefaultProtocol() +{ +} + +QString PdmlDefaultProtocol::pdmlProtoName() const +{ + return pdmlProtoName_; +} + +int PdmlDefaultProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +bool PdmlDefaultProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +int PdmlDefaultProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +void PdmlDefaultProtocol::preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + return; // do nothing! +} + +void PdmlDefaultProtocol::postProtocolHandler(OstProto::Stream *stream) +{ + return; // do nothing! +} + +void PdmlDefaultProtocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlAttributes &attributes, + OstProto::Stream *stream) +{ + return; // do nothing! +} + + +// ---------------------------------------------------------- // +// PdmlParser +// ---------------------------------------------------------- // +PdmlParser::PdmlParser(OstProto::StreamConfigList *streams) +{ + skipUntilEndOfPacket_ = false; + skipCount_ = 0; + + streams_ = streams; + + protocolMap_.insert("unknown", new PdmlUnknownProtocol()); + protocolMap_.insert("geninfo", new PdmlGenInfoProtocol()); + protocolMap_.insert("frame", new PdmlFrameProtocol()); + protocolMap_.insert("eth", new PdmlEthProtocol()); + protocolMap_.insert("ip", new PdmlIp4Protocol()); +} + +PdmlParser::~PdmlParser() +{ + // TODO: free protocolMap_.values() +} + +bool PdmlParser::startElement(const QString & /* namespaceURI */, + const QString & /* localName */, + const QString &qName, + const QXmlAttributes &attributes) +{ + qDebug("%s (%s)", __FUNCTION__, qName.toAscii().constData()); + + if (skipUntilEndOfPacket_) + { + Q_ASSERT(skipCount_ == 0); + goto _exit; + } + + if (skipCount_) + { + skipCount_++; + goto _exit; + } + + if (qName == "pdml") + { + packetCount_ = 0; + } + else if (qName == "packet") + { + Q_ASSERT(skipUntilEndOfPacket_ == false); + + // XXX: For now, each packet is converted to a stream + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + qDebug("packetCount_ = %d\n", packetCount_); + } + else if (qName == "proto") + { + QString protoName = attributes.value("name"); + if (protoName.isEmpty() + || (protoName == "expert")) + { + skipCount_++; + goto _exit; + } + + if (protoName == "fake-field-wrapper") + { + skipUntilEndOfPacket_ = true; + goto _exit; + } + + if (!protocolMap_.contains(protoName)) + protoName = "unknown"; // FIXME: change to Ost:Hexdump + + currentPdmlProtocol_ = protocolMap_.value(protoName); + + Q_ASSERT(currentPdmlProtocol_ != NULL); + + int protoId = currentPdmlProtocol_->ostProtoId(); + + currentPdmlProtocol_->preProtocolHandler(protoName, attributes, + currentStream_); + + if (protoId > 0) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = + proto->GetReflection(); + + const google::protobuf::FieldDescriptor *fDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + currentProtocolMsg_ = msgRefl->MutableMessage(proto, fDesc); + } + } + else if (qName == "field") + { + // fields with "hide='yes'" are informational and should be skipped + if (attributes.value("hide") == "yes") + { + skipCount_++; + goto _exit; + } + + QString name = attributes.value("name"); + int pos = attributes.value("pos").toInt(); + int size = attributes.value("size").toInt(); + QString valueStr = attributes.value("value"); + + // fields with no name are analysis and should be skipped + if (name.isEmpty()) + { + skipCount_++; + goto _exit; + } + + qDebug("\tname:%s, pos:%d, size:%d value:%s", + name.toAscii().constData(), + pos, + size, + valueStr.toAscii().constData()); + + if (!currentPdmlProtocol_->hasField(name)) + { + currentPdmlProtocol_->unknownFieldHandler(name, pos, size, + attributes, currentStream_); + goto _exit; + } + + // TODO + int fId = currentPdmlProtocol_->fieldId(name); + const google::protobuf::Descriptor *msgDesc = + currentProtocolMsg_->GetDescriptor(); + const google::protobuf::FieldDescriptor *fDesc = + msgDesc->FindFieldByNumber(fId); + const google::protobuf::Reflection *msgRefl = + currentProtocolMsg_->GetReflection(); + + bool isOk; + + switch(fDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(currentProtocolMsg_, fDesc, + valueStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(currentProtocolMsg_, fDesc, + valueStr.toULongLong(&isOk, kBaseHex)); + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fDesc->cpp_type()); + } + } + +_exit: + return true; + +} + +bool PdmlParser::characters(const QString &str) +{ + //qDebug("%s (%s)", __FUNCTION__, str.toAscii().constData()); + //_currentText += str; + return true; +} + +bool PdmlParser::endElement(const QString & /* namespaceURI */, + const QString & /* localName */, + const QString &qName) +{ + qDebug("%s (%s)", __FUNCTION__, qName.toAscii().constData()); + + if (qName == "packet") + { + packetCount_++; + if (skipUntilEndOfPacket_) + skipUntilEndOfPacket_ = false; + + goto _exit; + } + + if (skipUntilEndOfPacket_) + { + Q_ASSERT(skipCount_ == 0); + goto _exit; + } + + if (skipCount_) + { + skipCount_--; + goto _exit; + } + + if (qName == "proto") + { + currentPdmlProtocol_->postProtocolHandler(currentStream_); + } + else if (qName == "field") + { + } + +_exit: + return true; +} + +bool PdmlParser::fatalError(const QXmlParseException &exception) +{ + QString extra; + + qDebug("%s", __FUNCTION__); +#if 0 + if (exception.message() == "tag mismatch" && lastElement == "fieldData") + extra = "\nAre you using an old version of Wireshark? If so, try using a newer version. Alternatively, view the packet dump decode in Wireshark by clicking the \"External\" button."; +#endif + + QMessageBox::warning(0, QObject::tr("PDML Parser"), + QObject::tr("XML parse error for packet %1 " + "at line %2, column %3:\n %4\n%5") + .arg(packetCount_+1) + .arg(exception.lineNumber()) + .arg(exception.columnNumber()) + .arg(exception.message()) + .arg(extra)); + return false; +} + + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + pdmlProtoName_ = "OST:HexDump"; + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + bool isOk; + int pos = attributes.value("pos").toUInt(&isOk); + Q_ASSERT(isOk); + + int size = attributes.value("size").toUInt(&isOk); + Q_ASSERT(isOk); + + expPos_ = pos; + endPos_ = expPos_ + size; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + + // Skipped field? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + Q_ASSERT(expPos_ == endPos_); + + hexDump->set_pad_until_end(false); + expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + + qDebug("%s: %s, pos = %d, expPos_ = %d\n", __FUNCTION__, + name.toAscii().constData(), pos, expPos_); + + // Skipped field? Pad with zero! + if (pos > expPos_) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = + QByteArray::fromHex(attributes.value("value").toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ + pdmlProtoName_ = "geninfo"; +} + +void PdmlGenInfoProtocol::unknownFieldHandler(QString name, int pos, + int size, const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + stream->mutable_core()->set_frame_len(size+4); // TODO:check FCS +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ + pdmlProtoName_ = "frame"; +} + + +// ---------------------------------------------------------- // +// PdmlEthProtocol // +// ---------------------------------------------------------- // + +PdmlEthProtocol::PdmlEthProtocol() +{ + pdmlProtoName_ = "eth"; + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + if (name == "eth.type") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + bool isOk; + eth2->set_type(attributes.value("value").toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlIp4Protocol // +// ---------------------------------------------------------- // + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + pdmlProtoName_ = "ip"; + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", 5); + fieldMap_.insert("ip.dsfield", 6); + fieldMap_.insert("ip.len", 7); + fieldMap_.insert("ip.id", 8); + //fieldMap_.insert("ip.flags", 9); + fieldMap_.insert("ip.frag_offset", 10); + fieldMap_.insert("ip.ttl", 11); + fieldMap_.insert("ip.proto", 12); + fieldMap_.insert("ip.checksum", 13); + fieldMap_.insert("ip.src", 14); + fieldMap_.insert("ip.dst", 18); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + bool isOk; + + if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); // FIXME + ip4->set_is_override_hdrlen(true); // FIXME + ip4->set_is_override_totlen(true); // FIXME + ip4->set_is_override_proto(true); // FIXME + ip4->set_is_override_cksum(true); // FIXME +} + diff --git a/common/pdml_p.h b/common/pdml_p.h new file mode 100644 index 0000000..bfa7a01 --- /dev/null +++ b/common/pdml_p.h @@ -0,0 +1,132 @@ +/* +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 +*/ +#ifndef _PDML_P_H +#define _PDML_P_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +class QXmlSimpleReader; +class QXmlInputSource; + +class PdmlDefaultProtocol +{ +public: + PdmlDefaultProtocol(); + virtual ~PdmlDefaultProtocol(); + + QString pdmlProtoName() const; + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); + +protected: + QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ + int ostProtoId_; + QMap fieldMap_; +}; + +class PdmlParser : public QXmlDefaultHandler +{ +public: + PdmlParser(OstProto::StreamConfigList *streams); + ~PdmlParser(); + + bool startElement(const QString &namespaceURI, + const QString &localName, + const QString &qName, + const QXmlAttributes &attributes); + bool endElement(const QString &namespaceURI, + const QString &localName, + const QString &qName); + bool characters(const QString &str); + bool fatalError(const QXmlParseException &exception); + +private: + void initProtocolMaps(); + + QMap protocolMap_; + PdmlDefaultProtocol *currentPdmlProtocol_; + bool skipUntilEndOfPacket_; + int skipCount_; + int packetCount_; + OstProto::StreamConfigList *streams_; + + OstProto::Stream *currentStream_; + google::protobuf::Message *currentProtocolMsg_; +}; + +class PdmlUnknownProtocol : public PdmlDefaultProtocol +{ +public: + PdmlUnknownProtocol(); + + virtual void preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlDefaultProtocol +{ +public: + PdmlGenInfoProtocol(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); +}; + +class PdmlFrameProtocol : public PdmlDefaultProtocol +{ +public: + PdmlFrameProtocol(); +}; + +class PdmlEthProtocol : public PdmlDefaultProtocol +{ +public: + PdmlEthProtocol(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); +}; + +class PdmlIp4Protocol : public PdmlDefaultProtocol +{ +public: + PdmlIp4Protocol(); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); +}; +#endif diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..091924b --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,70 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" +#include "pdml_p.h" + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk; + QFile file(fileName); + PdmlParser *pdml; + QXmlSimpleReader *xmlReader; + QXmlInputSource *xmlSource; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + pdml = new PdmlParser(&streams); + xmlSource = new QXmlInputSource(&file); + xmlReader = new QXmlSimpleReader; + xmlReader->setContentHandler(pdml); + xmlReader->setErrorHandler(pdml); + isOk = xmlReader->parse(xmlSource, false); // non-incremental parse + + goto _exit; + +_open_fail: + isOk = false; + +_exit: + delete xmlReader; + delete xmlSource; + delete pdml; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + return false; +} + diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..d392c6b --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,44 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include + +class PdmlParser; + +class PdmlFileFormat : public QObject +{ + Q_OBJECT + +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..5e3f208 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,235 @@ +/* +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 +*/ + +package OstProto; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..e91f270 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,212 @@ +/* +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 +*/ + +#include "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" +#include "vlan.h" +#include "vlanstack.h" +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" +#include "ip4over4.h" +#include "ip6over6.h" +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" +#include "textproto.h" +#include "userscript.h" +#include "hexdump.h" +#include "sample.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..07b0604 --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,57 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..2a79660 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,534 @@ +/* +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 +*/ + +#include + +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +SampleProtocol::~SampleProtocol() +{ + delete configForm; +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +QWidget* SampleProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ +void SampleProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ +void SampleProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..91e6573 --- /dev/null +++ b/common/sample.h @@ -0,0 +1,102 @@ +/* +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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "sample.pb.h" +#include "ui_sample.h" + +#include "abstractprotocol.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + +public: + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..bdb80c3 --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,307 @@ +/* +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 +*/ + +#include +#include + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +SnapProtocol::~SnapProtocol() +{ + delete configForm; +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} + +QWidget* SnapProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideOui->setChecked( + fieldData(snap_is_override_oui, FieldValue).toBool()); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + + configForm->cbOverrideType->setChecked( + fieldData(snap_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + configWidget(); + + setFieldData(snap_is_override_oui, + configForm->cbOverrideOui->isChecked()); + setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + + setFieldData(snap_is_override_type, + configForm->cbOverrideType->isChecked()); + setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..daba802 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + +public: + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..b9dea25 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,489 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +quint32 StreamBase::packetRate() const +{ + return (quint32) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(quint32 packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +quint32 StreamBase::burstRate() const +{ + return (quint32) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(quint32 burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..7afbbd9 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,144 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + quint32 packetRate() const; + bool setPacketRate(quint32 packetsPerSec); + + quint32 burstRate() const; + bool setBurstRate(quint32 burstsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..893671d --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,68 @@ +/* +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 +*/ + +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..02faa6a --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,699 @@ +/* +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 +*/ + +#include +#include + +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +TcpProtocol::~TcpProtocol() +{ + delete configForm; +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* TcpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); + + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); + + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); + + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); + + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); + + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + int ff = 0; + + configWidget(); + + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); + + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); + + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); + + setFieldData(tcp_window, configForm->leTcpWindow->text()); + + setFieldData(tcp_cksum, configForm->leTcpCksum->text()); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); + + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + setFieldData(tcp_flags, ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..265937a --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..9834530 --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,295 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->eolCombo->setCurrentIndex( + fieldData(textProto_eol, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..1ec5fc0 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..f6996aa --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProtocol + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
intcombobox.h
+
+
+ + +
diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..8f3c35b --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,492 @@ +/* +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 +*/ + +#include +#include + +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +UdpProtocol::~UdpProtocol() +{ + delete configForm; +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* UdpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); + + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); + + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); + + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); + + setFieldData(udp_cksum, configForm->leUdpCksum->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..623e999 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,87 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..fa084f1 --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,609 @@ +/* +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 +*/ + +#include "userscript.h" + +#include + +// +// -------------------- UserScriptConfigForm -------------------- +// + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), protocol_(protocol) +{ + setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } + updateStatus(); +} + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + configForm = NULL; + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..4ef0d91 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,181 @@ +/* +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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; +}; + + + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + void updateStatus(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + + +class UserScriptProtocol : public AbstractProtocol +{ + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..95b2304 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,257 @@ +/* +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 +*/ + +#include + +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +VlanProtocol::~VlanProtocol() +{ + delete configForm; +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..728572b --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +protected: + OstProto::Vlan data; + +public: + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..3e0326d --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,179 @@ + + Vlan + + + + 0 + 0 + 268 + 96 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..c7b9989 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/qhexedit.h \ + src/qhexedit_p.h + +SOURCES = src/qhexedit.cpp \ + src/qhexedit_p.cpp diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..a295110 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,106 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(currentAddress(int)), this, SIGNAL(currentAddress(int))); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..2b94581 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,145 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.4.3 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text data. +There are sip configuration files included, so it is easy to create bindings +for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the +mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f) +you will change the data. Changed data is highlighted and can be accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and +insert data. In this case the size of data() increases. It is also possible to delete +bytes under the cursor, here the size of data decreases. + +There are some limitations: The size of data has in general to be below 10 megabytes, +otherwise the scroll sliders ard not shown and you can't scroll any more. Copy and +paste functionality is perhaps a subject of a later release. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwritem mode the user will overwrite existing data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + */ + void remove(int pos, int len=1); + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + void setFont(const QFont &); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + /*! \endcond docNever */ + +public slots: + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddress(int address); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..30f0660 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,414 @@ +#include + +#include "qhexedit_p.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setAddressAreaColor(QColor(Qt::lightGray).lighter(110)); + setHighlightingColor(QColor(Qt::yellow).lighter(160)); + + setFont(QFont("Mono", 10)); + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + + _cursorTimer.setInterval(500); + _cursorTimer.start(); + + setFocusPolicy(Qt::StrongFocus); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _addressOffset = offset; + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _addressOffset; +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _data = data; + _originalData = data; + adjust(); + setCursorPos(0); + setFocus(); +} + +QByteArray QHexEditPrivate::data() +{ + return _data; +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + if (overwriteMode != _overwriteMode) + { + emit overwriteModeChanged(overwriteMode); + _overwriteMode = overwriteMode; + adjust(); + } +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _originalData.insert(i, ba); +} + +void QHexEditPrivate::insert(int i, char ch) +{ + _data.insert(i, ch); + _originalData.insert(i, ch); +} + +void QHexEditPrivate::remove(int index, int len) +{ + _data.remove(index, len); + _originalData.remove(index, len); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + if ((addressWidth >= 0) and (addressWidth<=6)) + { + _addressNumbers = addressWidth; + adjust(); + setCursorPos(_cursorPosition); + } +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + bool down = false; + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + // calc address + + + // insert char + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + adjust(); + } + QByteArray hexValue = _data.mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + _data.replace(posBa, 1, QByteArray().fromHex(hexValue)); + emit dataChanged(); + + setCursorPos(_cursorPosition + 1); + down = true; + } + + // delete char + if (event->matches(QKeySequence::Delete)) + remove(posBa); + if (event->key() == Qt::Key_Backspace) + { + remove(posBa - 1); + setCursorPos(_cursorPosition - 2); + } + + // handle other function keys + if (event->key() == Qt::Key_Insert) + setOverwriteMode(!_overwriteMode); + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + down = true; + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + setCursorPos(_cursorPosition - 1); + if (event->matches(QKeySequence::MoveToStartOfLine)) + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + if (event->matches(QKeySequence::MoveToEndOfLine)) + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + if (event->matches(QKeySequence::MoveToPreviousLine)) + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToPreviousPage)) + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToStartOfDocument)) + setCursorPos(0); + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + down = true; + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_data.size() * 2); + down = true; + } + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + down = true; + } + + // when we move downwards, we have to go a little further + if (down) + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3 + _charHeight); + else + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3); + update(); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + setCursorPos(event->pos()); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _data.size()) + lastLineIdx = _data.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _addressOffset, _realAddressNumbers, 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + painter.setBackground(highLighted); + painter.setBackgroundMode(Qt::TransparentMode); + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + // hilight diff bytes + if (_highlighting) + { + int posBa = lineIdx + colIdx; + if (posBa >= _originalData.size()) + painter.setBackgroundMode(Qt::TransparentMode); + else + if (_data[posBa] == _originalData[posBa]) + painter.setBackgroundMode(Qt::TransparentMode); + else + painter.setBackgroundMode(Qt::OpaqueMode); + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + } + } + painter.setBackgroundMode(Qt::TransparentMode); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray ascii = _data.mid(lineIdx, BYTES_PER_LINE); + for (int idx=0; idx < ascii.size(); idx++) + if (((char)ascii[idx] < 0x20) or ((char)ascii[idx] > 0x7e)) + ascii[idx] = '.'; + painter.drawText(_xPosAscii, yPos, ascii); + } + } + + // paint cursor + if ((_data.size() > 0) and _blink) + painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText)); +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_data.size() * 2 - 1)) + position = _data.size() * 2 - 1; + } else { + if (position > (_data.size() * 2)) + position = _data.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddress(_cursorPosition/2); +} + +void QHexEditPrivate::setCursorPos(QPoint pos) +{ + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = (pos.y() / _charHeight) * 2 * BYTES_PER_LINE; + setCursorPos(x + y); + } +} + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _realAddressNumbers *_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + if (_overwriteMode) + _cursorWidth = _charWidth; + else + _cursorWidth = 2; + _cursorHeight = _charHeight - 3; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_data.size()/16 + 1) * _charHeight) + 3); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..c422c58 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,82 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressOffset(int offset); + int addressOffset(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void insert(int i, const QByteArray & ba); + void insert(int i, char ch); + void remove(int index, int len=1); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + +signals: + void currentAddress(int address); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *event); + void setCursorPos(QPoint pos); + void setCursorPos(int position); + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QByteArray _data; + QByteArray _originalData; + QColor _highlightingColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + + bool _blink; + bool _addressArea; + bool _asciiArea; + bool _highlighting; + bool _overwriteMode; + + int _addressNumbers, _realAddressNumbers; + int _addressOffset; + int _charWidth, _charHeight; + int _cursorX, _cursorY, _cursorWidth, _cursorHeight, _cursorPosition; + int _xPosAdr, _xPosHex, _xPosAscii; +}; + +/** \endcond docNever */ + +#endif + diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..0f9d987 --- /dev/null +++ b/ost.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..f53fa81 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..7c7789e --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[PB_HDR_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..e1fbdf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,39 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..2d1bd63 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,291 @@ +/* +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 +*/ + +//#include "pbhelper.h" +#include "rpcserver.h" +#include "pbqtio.h" + +#include + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcServer::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + +_exit: + delete controller; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + delete inStream; + delete outStream; + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcServer::when_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit2; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); + + parsing = false; + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..76f179a --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..d0e8c0c --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,249 @@ +/* +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 +*/ + +#include "abstractport.h" + +#include +#include + +#include "../common/streambase.h" + +AbstractPort::AbstractPort(int id, const char *device) +{ + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::updatePacketList() +{ + int len; + bool isVariable; + long sec = 0; + long usec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + long numPackets, numBursts; + long ibg, ipg; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->numBursts(); + numPackets = streamList_[i]->burstSize(); + ibg = 1000000/streamList_[i]->burstRate(); + ipg = 0; + break; + case OstProto::StreamControl::e_su_packets: + numBursts = 1; + numPackets = streamList_[i]->numPackets(); + ibg = 0; + ipg = 1000000/streamList_[i]->packetRate(); + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %ld, numPackets = %ld\n", + numBursts, numPackets); + qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + + if (streamList_[i]->isFrameVariable()) + { + isVariable = true; + len = 0; // avoid compiler warning; get len value for each pkt + } + else + { + isVariable = false; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), 0); + } + + for (int j = 0; j < numBursts; j++) + { + for (int k = 0; k < numPackets; k++) + { + if (isVariable) + { + len = streamList_[i]->frameValue(pktBuf_, + sizeof(pktBuf_), j * numPackets + k); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, sec, usec); + + appendToPacketList(sec, usec, pktBuf_, len); + + usec += ipg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numPackets) + + usec += ibg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numBursts) + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg : ipg); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; + stats->rxBytes = stats_.rxBytes - epochStats_.rxBytes; + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = stats_.txPkts - epochStats_.txPkts; + stats->txBytes = stats_.txBytes - epochStats_.txBytes; + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..e903007 --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,108 @@ +/* +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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + virtual void init(); + + int id() { return data_.port_id().id(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, long usecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + OstProto::Port data_; + OstProto::LinkState linkState_; + + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..8206ac2 --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,102 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + trayIcon_->hide(); + + delete trayIcon_; + delete trayIconMenu_; + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..7466a76 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include "ui_drone.h" + +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone +{ + Q_OBJECT + +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..160973b --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,45 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += drone.qrc +HEADERS += drone.h +FORMS += drone.ui +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone.qrc b/server/drone.qrc new file mode 100644 index 0000000..a642656 --- /dev/null +++ b/server/drone.qrc @@ -0,0 +1,5 @@ + + + icons/portgroup.png + + diff --git a/server/drone.ui b/server/drone.ui new file mode 100644 index 0000000..e2e0613 --- /dev/null +++ b/server/drone.ui @@ -0,0 +1,190 @@ + + Drone + + + + 0 + 0 + 268 + 216 + + + + Drone + + + :/icons/portgroup.png + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + (Server) + + + Qt::AlignCenter + + + + + + + TODO: Info/Status here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Exit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show + + + + + Exit + + + + + + + + + pushButton + clicked() + actionExit + trigger() + + + 134 + 194 + + + -1 + -1 + + + + + actionShow + triggered() + Drone + showNormal() + + + -1 + -1 + + + 133 + 107 + + + + + actionExit + triggered() + Drone + close() + + + -1 + -1 + + + 133 + 107 + + + + + diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..3f16bcc --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,49 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +int myport; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Drone drone; + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone.objectName()); + + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone.init()) + exit(-1); + + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone.showMinimized(); + app.exec(); + return 0; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..d4d1223 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,505 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + delete capturer_; + delete transmitter_; + delete monitorTx_; + delete monitorRx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
"); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
"); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "

%1
" + "Rx/Tx Rates are also subject to above limitation(s)

"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE]; + + direction_ = direction; + isDirectional_ = true; + stats_ = stats; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); +} + +void PcapPort::PortMonitor::run() +{ + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for sendQueueList + while(sendQueueList_.size()) + { + pcap_send_queue *sq = sendQueueList_.takeFirst(); + pcap_sendqueue_destroy(sq); + } + setPacketListLoopMode(false, 0); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + pcap_send_queue *sendQ; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = usec; + + sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); + + if ((sendQ == NULL) || + (sendQ->len + sizeof(pcap_pkthdr) + length) > sendQ->maxlen) + { + //! \todo (LOW): calculate sendqueue size + sendQ = pcap_sendqueue_alloc(1*1024*1024); + sendQueueList_.append(sendQ); + + // Validate that the pkt will fit inside the new sendQ + Q_ASSERT((length + sizeof(pcap_pkthdr)) < sendQ->maxlen); + } + + if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) + op = false; + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + + qDebug("sendQueueList_.size = %d", sendQueueList_.size()); + if (sendQueueList_.size() <= 0) + return; + + for(i = 0; i < sendQueueList_.size(); i++) + { + int ret; +_restart: + ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); + + if (ret < 0) + { + qDebug("error %d in sendQueueTransmit()", ret); + stop_ = false; + return; + } + } + + if (returnToQIdx_ >= 0) + { + i = returnToQIdx_; + + udelay(loopDelay_); + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, int sync) +{ + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + while (1) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (stop_) + { + return -2; + } + + // A pktLen of size 0 is used at the end of a sendQueue and before + // the next sendQueue - i.e. for inter sendQueue timing + if(pktLen > 0) + { + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + } + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + // Check if the end of the user buffer has been reached + if((char*) hdr >= end) + return 0; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + if (usec) + { + udelay(usec); + ts = hdr->ts; + } + } + } +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#ifdef Q_OS_WIN32 + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } + + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); + if (handle_ == NULL) + { + qDebug("Error opening port %s: %s\n", + device_.toAscii().constData(), pcap_geterr(handle_)); + return; + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..a6264c5 --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,149 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0); + } + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, usec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, long usecDelay) { + transmitter_->setPacketListLoopMode(loop, usecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + protected: + AbstractPort::PortStats *stats_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, long usecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = usecDelay; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + + quint64 ticksFreq_; + QList sendQueueList_; + int returnToQIdx_; + long loopDelay_; + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..b4f7572 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,77 @@ +/* +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 +*/ + +#include "portmanager.h" + +#include +#include + +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + +#ifdef Q_OS_WIN32 + port = new WinPcapPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + port->init(); + portList_.append(port); + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + } + + pcap_freealldevs(deviceList); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..3f38a15 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,215 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..7422c3c --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.3 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From a70a6c53504f5966f0137b927d81843a2b79ba0b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 10 Feb 2011 23:30:58 +0530 Subject: [PATCH 117/294] non crashing code - snapshot before trying QXmlStreamReader --- common/pdml_p.cpp | 329 ++++++++++++++++++++++++++++++++++++-------- common/pdml_p.h | 42 +++++- server/pcapport.cpp | 2 +- 3 files changed, 311 insertions(+), 62 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 4d8eee7..e887f33 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -21,8 +21,10 @@ along with this program. If not, see #include "mac.pb.h" #include "eth2.pb.h" -#include "ip4.pb.h" #include "hexdump.pb.h" +#include "ip4.pb.h" +#include "ip6.pb.h" +#include "tcp.pb.h" #include @@ -85,16 +87,24 @@ void PdmlDefaultProtocol::unknownFieldHandler(QString name, // ---------------------------------------------------------- // PdmlParser::PdmlParser(OstProto::StreamConfigList *streams) { - skipUntilEndOfPacket_ = false; skipCount_ = 0; + currentPdmlProtocol_ = NULL; streams_ = streams; protocolMap_.insert("unknown", new PdmlUnknownProtocol()); protocolMap_.insert("geninfo", new PdmlGenInfoProtocol()); protocolMap_.insert("frame", new PdmlFrameProtocol()); +#if 0 + protocolMap_.insert("fake-field-wrapper", + new PdmlFakeFieldWrapperProtocol()); +#endif +#if 0 protocolMap_.insert("eth", new PdmlEthProtocol()); protocolMap_.insert("ip", new PdmlIp4Protocol()); + protocolMap_.insert("ipv6", new PdmlIp6Protocol()); + protocolMap_.insert("tcp", new PdmlTcpProtocol()); +#endif } PdmlParser::~PdmlParser() @@ -109,12 +119,6 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, { qDebug("%s (%s)", __FUNCTION__, qName.toAscii().constData()); - if (skipUntilEndOfPacket_) - { - Q_ASSERT(skipCount_ == 0); - goto _exit; - } - if (skipCount_) { skipCount_++; @@ -127,8 +131,6 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, } else if (qName == "packet") { - Q_ASSERT(skipUntilEndOfPacket_ == false); - // XXX: For now, each packet is converted to a stream currentStream_ = streams_->add_stream(); currentStream_->mutable_stream_id()->set_id(packetCount_); @@ -146,11 +148,11 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, goto _exit; } - if (protoName == "fake-field-wrapper") - { - skipUntilEndOfPacket_ = true; - goto _exit; - } + // HACK HACK HACK + if (currentPdmlProtocol_ && (currentPdmlProtocol_->ostProtoId() + == OstProto::Protocol::kHexDumpFieldNumber)) + currentPdmlProtocol_->postProtocolHandler(currentStream_); + if (!protocolMap_.contains(protoName)) protoName = "unknown"; // FIXME: change to Ost:Hexdump @@ -161,25 +163,26 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, int protoId = currentPdmlProtocol_->ostProtoId(); + // PdmlDefaultProtocol => + if (protoId <= 0) + goto _exit; + + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = + proto->GetReflection(); + const google::protobuf::FieldDescriptor *fDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + currentProtocolMsg_ = msgRefl->MutableMessage(proto, fDesc); + currentPdmlProtocol_->preProtocolHandler(protoName, attributes, currentStream_); - if (protoId > 0) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id(protoId); - - const google::protobuf::Reflection *msgRefl = - proto->GetReflection(); - - const google::protobuf::FieldDescriptor *fDesc = - msgRefl->FindKnownExtensionByNumber(protoId); - - // TODO: if !fDesc - // init default values of all fields in protocol - currentProtocolMsg_ = msgRefl->MutableMessage(proto, fDesc); - } } else if (qName == "field") { @@ -191,16 +194,14 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, } QString name = attributes.value("name"); - int pos = attributes.value("pos").toInt(); - int size = attributes.value("size").toInt(); QString valueStr = attributes.value("value"); + int pos = -1; + int size = -1; - // fields with no name are analysis and should be skipped - if (name.isEmpty()) - { - skipCount_++; - goto _exit; - } + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toInt(); qDebug("\tname:%s, pos:%d, size:%d value:%s", name.toAscii().constData(), @@ -236,6 +237,14 @@ bool PdmlParser::startElement(const QString & /* namespaceURI */, case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: msgRefl->SetUInt64(currentProtocolMsg_, fDesc, valueStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(currentProtocolMsg_, fDesc, str); + break; + } default: qDebug("%s: unhandled cpptype = %d", __FUNCTION__, fDesc->cpp_type()); @@ -262,16 +271,21 @@ bool PdmlParser::endElement(const QString & /* namespaceURI */, if (qName == "packet") { + if (currentStream_->core().name().size()) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->set_content(currentStream_->core().name()); + hexDump->set_pad_until_end(false); + currentStream_->mutable_core()->set_name(""); + } packetCount_++; - if (skipUntilEndOfPacket_) - skipUntilEndOfPacket_ = false; - - goto _exit; - } - - if (skipUntilEndOfPacket_) - { - Q_ASSERT(skipCount_ == 0); + currentPdmlProtocol_ = NULL; goto _exit; } @@ -284,6 +298,7 @@ bool PdmlParser::endElement(const QString & /* namespaceURI */, if (qName == "proto") { currentPdmlProtocol_->postProtocolHandler(currentStream_); + //currentPdmlProtocol_ = NULL; } else if (qName == "field") { @@ -331,14 +346,22 @@ void PdmlUnknownProtocol::preProtocolHandler(QString name, const QXmlAttributes &attributes, OstProto::Stream *stream) { bool isOk; + int size; int pos = attributes.value("pos").toUInt(&isOk); - Q_ASSERT(isOk); + if (!isOk) + goto _skip_pos_size_proc; - int size = attributes.value("size").toUInt(&isOk); - Q_ASSERT(isOk); + size = attributes.value("size").toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; expPos_ = pos; endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); } void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) @@ -346,7 +369,7 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) OstProto::HexDump *hexDump = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - // Skipped field? Pad with zero! + // Skipped field(s) at end? Pad with zero! if (endPos_ > expPos_) { QByteArray hexVal(endPos_ - expPos_, char(0)); @@ -355,10 +378,11 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) expPos_ += hexVal.size(); } - Q_ASSERT(expPos_ == endPos_); + qDebug("%s: expPos_ = %d, endPos_ = %d\n", __FUNCTION__, expPos_, endPos_); + //Q_ASSERT(expPos_ == endPos_); hexDump->set_pad_until_end(false); - expPos_ = -1; + endPos_ = expPos_ = -1; } void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, @@ -367,11 +391,12 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, OstProto::HexDump *hexDump = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - qDebug("%s: %s, pos = %d, expPos_ = %d\n", __FUNCTION__, - name.toAscii().constData(), pos, expPos_); + qDebug("%s: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + __PRETTY_FUNCTION__, name.toAscii().constData(), + pos, expPos_, endPos_); // Skipped field? Pad with zero! - if (pos > expPos_) + if ((pos > expPos_) && (expPos_ < endPos_)) { QByteArray hexVal(pos - expPos_, char(0)); @@ -379,10 +404,11 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, expPos_ += hexVal.size(); } - if (pos == expPos_) + if ((pos == expPos_) /*&& (pos < endPos_)*/) { - QByteArray hexVal = - QByteArray::fromHex(attributes.value("value").toUtf8()); + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toUtf8()); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); @@ -414,7 +440,59 @@ PdmlFrameProtocol::PdmlFrameProtocol() pdmlProtoName_ = "frame"; } +#if 1 +// ---------------------------------------------------------- // +// PdmlFakeFieldWrapperProtocol // +// ---------------------------------------------------------- // +PdmlFakeFieldWrapperProtocol::PdmlFakeFieldWrapperProtocol() +{ + pdmlProtoName_ = "OST:HexDump"; + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + expPos_ = -1; +} + +void PdmlFakeFieldWrapperProtocol::preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + expPos_ = 0; + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlFakeFieldWrapperProtocol::postProtocolHandler(OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + + qDebug("%s: expPos_ = %d\n", __FUNCTION__, expPos_); + + hexDump->set_pad_until_end(false); + expPos_ = -1; +} + +void PdmlFakeFieldWrapperProtocol::unknownFieldHandler(QString name, int pos, + int size, const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + + if ((pos == expPos_) && (size >= 0) && + (!attributes.value("unmaskedvalue").isEmpty() || + !attributes.value("value").isEmpty())) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + +#endif // ---------------------------------------------------------- // // PdmlEthProtocol // // ---------------------------------------------------------- // @@ -495,3 +573,134 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Stream *stream) ip4->set_is_override_cksum(true); // FIXME } +// ---------------------------------------------------------- // +// PdmlIp6Protocol // +// ---------------------------------------------------------- // + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + pdmlProtoName_ = "ipv6"; + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value"); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value"); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Stream *stream) +{ + OstProto::Ip6 *ip6 = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); // FIXME + ip6->set_is_override_payload_length(true); // FIXME + ip6->set_is_override_next_header(true); // FIXME +} + +// ---------------------------------------------------------- // +// PdmlTcpProtocol // +// ---------------------------------------------------------- // + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + pdmlProtoName_ = "tcp"; + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toUtf8()); + else if (name == "" + && attributes.value("show").startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toUtf8()); + stream->mutable_core()->mutable_name()->append(segmentData_.constData(), + segmentData_.size()); + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::tcp); + + tcp->set_is_override_src_port(true); // FIXME + tcp->set_is_override_dst_port(true); // FIXME + tcp->set_is_override_hdrlen(true); // FIXME + tcp->set_is_override_cksum(true); // FIXME + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } + +#if 0 + if (segmentData_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(segmentData_.constData(), + segmentData_.size()); + hexDump->set_pad_until_end(false); + segmentData_.resize(0); + } +#endif +} + diff --git a/common/pdml_p.h b/common/pdml_p.h index bfa7a01..e125dd6 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -21,11 +21,14 @@ along with this program. If not, see #include "protocol.pb.h" +#include #include #include #include #include +// TODO: add const where possible + class QXmlSimpleReader; class QXmlInputSource; @@ -73,7 +76,6 @@ private: QMap protocolMap_; PdmlDefaultProtocol *currentPdmlProtocol_; - bool skipUntilEndOfPacket_; int skipCount_; int packetCount_; OstProto::StreamConfigList *streams_; @@ -112,6 +114,22 @@ public: PdmlFrameProtocol(); }; +#if 1 +class PdmlFakeFieldWrapperProtocol : public PdmlDefaultProtocol +{ +public: + PdmlFakeFieldWrapperProtocol(); + + virtual void preProtocolHandler(QString name, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); +private: + int expPos_; +}; +#endif + class PdmlEthProtocol : public PdmlDefaultProtocol { public: @@ -129,4 +147,26 @@ public: const QXmlAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); }; + +class PdmlIp6Protocol : public PdmlDefaultProtocol +{ +public: + PdmlIp6Protocol(); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); +}; + +class PdmlTcpProtocol : public PdmlDefaultProtocol +{ +public: + PdmlTcpProtocol(); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlAttributes &attributes, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Stream *stream); +private: + QByteArray options_; + QByteArray segmentData_; +}; + #endif diff --git a/server/pcapport.cpp b/server/pcapport.cpp index d4d1223..0c283c0 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -311,7 +311,7 @@ void PcapPort::PortTransmitter::run() // 'stats callback' function so that both Rx and Tx stats are updated // together - const int kSyncTransmit = 1; + const int kSyncTransmit = 0; // temp mask, shd be 1; int i; qDebug("sendQueueList_.size = %d", sendQueueList_.size()); From 5ab89bba296718c38f12436baf9ab231f175a0b7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 18 Feb 2011 23:32:57 +0530 Subject: [PATCH 118/294] snapshot --- .hgignore | 2 + client/port.cpp | 7 +- common/ostproto.pro | 2 + common/pcapfileformat.cpp | 252 +++++++++++++++++++ common/pcapfileformat.h | 42 ++++ common/pdml_p.cpp | 500 +++++++++++++++++++++++++++++++++++--- common/pdml_p.h | 82 ++++++- common/pdmlfileformat.cpp | 27 ++ 8 files changed, 871 insertions(+), 43 deletions(-) create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h diff --git a/.hgignore b/.hgignore index cd5490f..c268bf1 100644 --- a/.hgignore +++ b/.hgignore @@ -31,3 +31,5 @@ version.cpp # ctags tags + +pcaps\* diff --git a/client/port.cpp b/client/port.cpp index c1e402c..e3c873b 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -20,7 +20,7 @@ along with this program. If not, see #include "port.h" #include "fileformat.h" -#include "pdmlfileformat.h" +#include "pcapfileformat.h" #include #include @@ -229,7 +229,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error) OstProto::StreamConfigList streams; //if (!fileFormat.openStreams(fileName, streams, error)) - if (!pdmlFileFormat.openStreams(fileName, streams, error)) + if (!pcapFileFormat.openStreams(fileName, streams, error)) goto _fail; if (!append) @@ -262,5 +262,6 @@ bool Port::saveStreams(QString fileName, QString &error) mStreams[i]->protoDataCopyInto(*s); } - return fileFormat.saveStreams(streams, fileName, error); + //return fileFormat.saveStreams(streams, fileName, error); + return pcapFileFormat.saveStreams(streams, fileName, error); } diff --git a/common/ostproto.pro b/common/ostproto.pro index 26aefae..ef829da 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -58,6 +58,7 @@ HEADERS += \ abstractprotocol.h \ comboprotocol.h \ fileformat.h \ + pcapfileformat.h \ pdmlfileformat.h \ pdml_p.h \ protocolmanager.h \ @@ -98,6 +99,7 @@ SOURCES += \ abstractprotocol.cpp \ crc32c.cpp \ fileformat.cpp \ + pcapfileformat.cpp \ pdmlfileformat.cpp \ pdml_p.cpp \ protocolmanager.cpp \ diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..3ef8c76 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,252 @@ +/* +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 +*/ + +#include "pcapfileformat.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ +} PcapFileHeader; + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; + +typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ +} PcapPacketHeader; + +PcapFileFormat pcapFileFormat; + +PcapFileFormat::PcapFileFormat() +{ +} + +PcapFileFormat::~PcapFileFormat() +{ +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QDataStream fd; + quint32 magic; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + quint32 len; + int pktCount = 1; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + if (file.size() < sizeof(fileHdr)) + goto _err_truncated; + + fd.setDevice(&file); + + fd >> magic; + + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd.byteOrder() == QDataStream::BigEndian) + fd.setByteOrder(QDataStream::LittleEndian); + else + fd.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd >> fileHdr.versionMajor; + fd >> fileHdr.versionMinor; + fd >> fileHdr.thisZone; + fd >> fileHdr.sigfigs; + fd >> fileHdr.snapLen; + fd >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + + // TODO: what do we do about non-ethernet networks? + + pktBuf.resize(fileHdr.snapLen); + + while (!fd.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + // read PcapPacketHeader + fd >> pktHdr.tsSec; + fd >> pktHdr.tsUsec; + fd >> pktHdr.inclLen; + fd >> pktHdr.origLen; + + // TODO: chk fd.status() + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + // read Pkt contents + len = fd.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + pktCount++; + } + + isOk = true; + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QDataStream fd; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = 1; // Ethernet; FIXME: Hardcoding + + fd << fileHdr.magicNumber; + fd << fileHdr.versionMajor; + fd << fileHdr.versionMinor; + fd << fileHdr.thisZone; + fd << fileHdr.sigfigs; + fd << fileHdr.snapLen; + fd << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + // TODO: write actual timing!?!? + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + pktHdr.inclLen = pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd << pktHdr.tsSec; + fd << pktHdr.tsUsec; + fd << pktHdr.inclLen; + fd << pktHdr.origLen; + fd.writeRawData(pktBuf.data(), pktHdr.inclLen); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..a515e32 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include + +class PcapFileFormat : public QObject +{ + Q_OBJECT + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index e887f33..fbc4a0e 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -43,6 +43,11 @@ PdmlDefaultProtocol::~PdmlDefaultProtocol() { } +PdmlDefaultProtocol* PdmlDefaultProtocol::createInstance() +{ + return new PdmlDefaultProtocol(); +} + QString PdmlDefaultProtocol::pdmlProtoName() const { return pdmlProtoName_; @@ -64,7 +69,12 @@ int PdmlDefaultProtocol::fieldId(QString name) const } void PdmlDefaultProtocol::preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) +{ + return; // do nothing! +} + +void PdmlDefaultProtocol::prematureEndHandler(int pos, OstProto::Stream *stream) { return; // do nothing! } @@ -75,13 +85,14 @@ void PdmlDefaultProtocol::postProtocolHandler(OstProto::Stream *stream) } void PdmlDefaultProtocol::unknownFieldHandler(QString name, - int pos, int size, const QXmlAttributes &attributes, + int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { return; // do nothing! } +#if 0 // ---------------------------------------------------------- // // PdmlParser // ---------------------------------------------------------- // @@ -328,6 +339,392 @@ bool PdmlParser::fatalError(const QXmlParseException &exception) .arg(extra)); return false; } +#endif + +// ---------------------------------------------------------- // +// PdmlReader // +// ---------------------------------------------------------- // +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + streams_ = streams; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); +#if 0 + factory_.insert("fake-field-wrapper", + new PdmlFakeFieldWrapperProtocol()); +#endif + factory_.insert("eth",PdmlEthProtocol::createInstance); + factory_.insert("ip",PdmlIp4Protocol::createInstance); + factory_.insert("ipv6",PdmlIp6Protocol::createInstance); + factory_.insert("tcp",PdmlTcpProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device) +{ + setDevice(device); + packetCount_ = 0; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + return !error(); +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlDefaultProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlDefaultProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName = attributes().value("name").toString(); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + else + return false; +} + +void PdmlReader::readUnexpectedElement() +{ + Q_ASSERT(isStartElement()); + + // XXX: add to 'log' + qDebug("unexpected element - <%s>; skipping ...", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + if (isEndElement()) + break; + + if (isStartElement()) + readUnexpectedElement(); + } +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + // XXX: add to 'log' + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + Q_ASSERT(isStartElement() && name() == "pdml"); + + packetCount_ = 0; + + while (!atEnd()) + { + readNext(); + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + readUnexpectedElement(); + } + } +} + +void PdmlReader::readPacket() +{ + Q_ASSERT(isStartElement() && name() == "packet"); + + packetCount_++; + qDebug("packetNum = %d", packetCount_); + + // XXX: For now, each packet is converted to a stream + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + readUnexpectedElement(); + } + } + + if (currentStream_->core().name().size()) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->set_content(currentStream_->core().name()); + hexDump->set_pad_until_end(false); + currentStream_->mutable_core()->set_name(""); + } +} + + +void PdmlReader::readProto() +{ + PdmlDefaultProtocol *pdmlProto = NULL; + google::protobuf::Message *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName = attributes().value("name").toString(); + qDebug("proto: %s", protoName.toAscii().constData()); + +#if 0 + if (protoName.isEmpty() || (protoName == "expert")) + { + skipElement(); + return; + } +#endif + + pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + // Non PdmlDefaultProtocol + if (protoId > 0) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + pbProto = msgRefl->MutableMessage(proto, fieldDesc); + + } + + pdmlProto->preProtocolHandler(protoName, attributes(), currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + int endPos = -1; + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + if (isDontCareProto()) + { + skipElement(); + continue; + } + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + + // pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + pdmlProto->prematureEndHandler(endPos, currentStream_); + pdmlProto->postProtocolHandler(currentStream_); + } + readProto(); + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if (pdmlProto == NULL) + { + pdmlProto = allocPdmlProtocol(protoName); + protoId = pdmlProto->ostProtoId(); + + // Non PdmlDefaultProtocol + if (protoId > 0) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + pbProto = msgRefl->MutableMessage(proto, fieldDesc); + + } + pdmlProto->preProtocolHandler(protoName, attributes(), currentStream_); + } + readField(pdmlProto, pbProto); + } + else + readUnexpectedElement(); + } + } + + if (pdmlProto) + { + pdmlProto->postProtocolHandler(currentStream_); + freePdmlProtocol(pdmlProto); + } +} + +void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, + google::protobuf::Message *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + QString valueHexStr = attributes().value("value").toString(); + int pos = -1; + int size = -1; + + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("\tfieldName:%s, pos:%d, size:%d value:%s", + fieldName.toAscii().constData(), + pos, + size, + valueHexStr.toAscii().constData()); + + if (pdmlProto->hasField(fieldName)) + { + int fieldId = pdmlProto->fieldId(fieldName); + const google::protobuf::Descriptor *msgDesc = + pbProto->GetDescriptor(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgDesc->FindFieldByNumber(fieldId); + const google::protobuf::Reflection *msgRefl = + pbProto->GetReflection(); + + bool isOk; + + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(pbProto, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(pbProto, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(pbProto, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } + } + else + { + pdmlProto->unknownFieldHandler(fieldName, pos, size, attributes(), + currentStream_); + } + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + if (isDontCareProto()) + { + skipElement(); + continue; + } + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + readUnexpectedElement(); + } + } +} // ---------------------------------------------------------- // @@ -336,22 +733,27 @@ bool PdmlParser::fatalError(const QXmlParseException &exception) PdmlUnknownProtocol::PdmlUnknownProtocol() { - pdmlProtoName_ = "OST:HexDump"; + pdmlProtoName_ = ""; ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; endPos_ = expPos_ = -1; } +PdmlDefaultProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + void PdmlUnknownProtocol::preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { bool isOk; int size; - int pos = attributes.value("pos").toUInt(&isOk); + int pos = attributes.value("pos").toString().toUInt(&isOk); if (!isOk) goto _skip_pos_size_proc; - size = attributes.value("size").toUInt(&isOk); + size = attributes.value("size").toString().toUInt(&isOk); if (!isOk) goto _skip_pos_size_proc; @@ -364,6 +766,11 @@ _skip_pos_size_proc: hexDump->set_pad_until_end(false); } +void PdmlUnknownProtocol::prematureEndHandler(int pos, OstProto::Stream *stream) +{ + endPos_ = pos; +} + void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) { OstProto::HexDump *hexDump = stream->mutable_protocol( @@ -378,7 +785,7 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) expPos_ += hexVal.size(); } - qDebug("%s: expPos_ = %d, endPos_ = %d\n", __FUNCTION__, expPos_, endPos_); + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); //Q_ASSERT(expPos_ == endPos_); hexDump->set_pad_until_end(false); @@ -386,13 +793,13 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) } void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { OstProto::HexDump *hexDump = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - qDebug("%s: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", - __PRETTY_FUNCTION__, name.toAscii().constData(), + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), pos, expPos_, endPos_); // Skipped field? Pad with zero! @@ -407,8 +814,8 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, if ((pos == expPos_) /*&& (pos < endPos_)*/) { QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? - QByteArray::fromHex(attributes.value("value").toUtf8()) : - QByteArray::fromHex(attributes.value("unmaskedvalue").toUtf8()); + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); @@ -425,8 +832,13 @@ PdmlGenInfoProtocol::PdmlGenInfoProtocol() pdmlProtoName_ = "geninfo"; } +PdmlDefaultProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + void PdmlGenInfoProtocol::unknownFieldHandler(QString name, int pos, - int size, const QXmlAttributes &attributes, OstProto::Stream *stream) + int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { stream->mutable_core()->set_frame_len(size+4); // TODO:check FCS } @@ -440,6 +852,11 @@ PdmlFrameProtocol::PdmlFrameProtocol() pdmlProtoName_ = "frame"; } +PdmlDefaultProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + #if 1 // ---------------------------------------------------------- // // PdmlFakeFieldWrapperProtocol // @@ -453,8 +870,13 @@ PdmlFakeFieldWrapperProtocol::PdmlFakeFieldWrapperProtocol() expPos_ = -1; } +PdmlDefaultProtocol* PdmlFakeFieldWrapperProtocol::createInstance() +{ + return new PdmlFakeFieldWrapperProtocol(); +} + void PdmlFakeFieldWrapperProtocol::preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { expPos_ = 0; OstProto::HexDump *hexDump = stream->mutable_protocol( @@ -474,7 +896,7 @@ void PdmlFakeFieldWrapperProtocol::postProtocolHandler(OstProto::Stream *stream) } void PdmlFakeFieldWrapperProtocol::unknownFieldHandler(QString name, int pos, - int size, const QXmlAttributes &attributes, OstProto::Stream *stream) + int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { OstProto::HexDump *hexDump = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); @@ -484,8 +906,8 @@ void PdmlFakeFieldWrapperProtocol::unknownFieldHandler(QString name, int pos, !attributes.value("value").isEmpty())) { QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? - QByteArray::fromHex(attributes.value("value").toUtf8()) : - QByteArray::fromHex(attributes.value("unmaskedvalue").toUtf8()); + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); @@ -506,8 +928,13 @@ PdmlEthProtocol::PdmlEthProtocol() fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); } +PdmlDefaultProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { if (name == "eth.type") { @@ -519,7 +946,7 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); bool isOk; - eth2->set_type(attributes.value("value").toUInt(&isOk, kBaseHex)); + eth2->set_type(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); eth2->set_is_override_type(true); } } @@ -547,8 +974,13 @@ PdmlIp4Protocol::PdmlIp4Protocol() fieldMap_.insert("ip.dst", 18); } +PdmlDefaultProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { bool isOk; @@ -557,7 +989,7 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, OstProto::Ip4 *ip4 = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::ip4); - ip4->set_flags(attributes.value("value").toUInt(&isOk, kBaseHex) >> 5); + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); } } @@ -592,8 +1024,13 @@ PdmlIp6Protocol::PdmlIp6Protocol() // ipv6.src and ipv6.dst handled as unknown fields } +PdmlDefaultProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { bool isOk; @@ -601,7 +1038,7 @@ void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, { OstProto::Ip6 *ip6 = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::ip6); - QString addrHexStr = attributes.value("value"); + QString addrHexStr = attributes.value("value").toString(); ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); @@ -610,7 +1047,7 @@ void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, { OstProto::Ip6 *ip6 = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::ip6); - QString addrHexStr = attributes.value("value"); + QString addrHexStr = attributes.value("value").toString(); ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); @@ -647,15 +1084,20 @@ PdmlTcpProtocol::PdmlTcpProtocol() fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); } +PdmlDefaultProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { if (name == "tcp.options") - options_ = QByteArray::fromHex(attributes.value("value").toUtf8()); + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); else if (name == "" - && attributes.value("show").startsWith("TCP segment data")) + && attributes.value("show").toString().startsWith("TCP segment data")) { - segmentData_ = QByteArray::fromHex(attributes.value("value").toUtf8()); + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); stream->mutable_core()->mutable_name()->append(segmentData_.constData(), segmentData_.size()); } @@ -666,6 +1108,8 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Stream *stream) OstProto::Tcp *tcp = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::tcp); + qDebug("Tcp: post\n"); + tcp->set_is_override_src_port(true); // FIXME tcp->set_is_override_dst_port(true); // FIXME tcp->set_is_override_hdrlen(true); // FIXME diff --git a/common/pdml_p.h b/common/pdml_p.h index e125dd6..174442c 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -26,6 +26,7 @@ along with this program. If not, see #include #include #include +#include // TODO: add const where possible @@ -35,19 +36,22 @@ class QXmlInputSource; class PdmlDefaultProtocol { public: - PdmlDefaultProtocol(); + PdmlDefaultProtocol(); // TODO: make private virtual ~PdmlDefaultProtocol(); + static PdmlDefaultProtocol* createInstance(); + QString pdmlProtoName() const; int ostProtoId() const; bool hasField(QString name) const; int fieldId(QString name) const; virtual void preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); protected: QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ @@ -55,6 +59,7 @@ protected: QMap fieldMap_; }; +#if 0 class PdmlParser : public QXmlDefaultHandler { public: @@ -83,17 +88,53 @@ private: OstProto::Stream *currentStream_; google::protobuf::Message *currentProtocolMsg_; }; +#endif + +class PdmlReader : public QXmlStreamReader +{ +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device); + +private: + PdmlDefaultProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlDefaultProtocol *proto); + + bool isDontCareProto(); + void readPdml(); + void skipElement(); + void readUnexpectedElement(); + void readPacket(); + void readProto(); + void readField(PdmlDefaultProtocol *pdmlProto, + google::protobuf::Message *pbProto); + + typedef PdmlDefaultProtocol* (*FactoryMethod)(); + QMap factory_; + + OstProto::StreamConfigList *streams_; + + int packetCount_; + OstProto::Stream *currentStream_; + //PdmlDefaultProtocol *currentPdmlProtocol_; + //google::protobuf::Message *currentProtocolMsg_; +}; class PdmlUnknownProtocol : public PdmlDefaultProtocol { public: PdmlUnknownProtocol(); + static PdmlDefaultProtocol* createInstance(); + virtual void preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); private: int endPos_; int expPos_; @@ -104,14 +145,18 @@ class PdmlGenInfoProtocol : public PdmlDefaultProtocol public: PdmlGenInfoProtocol(); + static PdmlDefaultProtocol* createInstance(); + virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); }; class PdmlFrameProtocol : public PdmlDefaultProtocol { public: PdmlFrameProtocol(); + + static PdmlDefaultProtocol* createInstance(); }; #if 1 @@ -120,11 +165,13 @@ class PdmlFakeFieldWrapperProtocol : public PdmlDefaultProtocol public: PdmlFakeFieldWrapperProtocol(); + static PdmlDefaultProtocol* createInstance(); + virtual void preProtocolHandler(QString name, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); private: int expPos_; }; @@ -135,16 +182,21 @@ class PdmlEthProtocol : public PdmlDefaultProtocol public: PdmlEthProtocol(); + static PdmlDefaultProtocol* createInstance(); + virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); }; class PdmlIp4Protocol : public PdmlDefaultProtocol { public: PdmlIp4Protocol(); + + static PdmlDefaultProtocol* createInstance(); + virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); }; @@ -152,8 +204,11 @@ class PdmlIp6Protocol : public PdmlDefaultProtocol { public: PdmlIp6Protocol(); + + static PdmlDefaultProtocol* createInstance(); + virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); }; @@ -161,8 +216,11 @@ class PdmlTcpProtocol : public PdmlDefaultProtocol { public: PdmlTcpProtocol(); + + static PdmlDefaultProtocol* createInstance(); + virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); private: QByteArray options_; diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 091924b..3f86084 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -30,6 +30,7 @@ PdmlFileFormat::~PdmlFileFormat() { } +#if 0 bool PdmlFileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { @@ -61,6 +62,32 @@ _exit: return isOk; } +#else +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + // TODO: fill in error string + + isOk = reader->read(&file); + + goto _exit; + +_open_fail: + isOk = false; + +_exit: + delete reader; + + return isOk; +} +#endif bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) From 73afecc07acf1b37bf4136513e0524028b8e76b4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 20 Feb 2011 13:10:33 +0530 Subject: [PATCH 119/294] Fix for crash when NIC doesn't support setting promisc mode --- server/abstractport.cpp | 1 + server/abstractport.h | 3 +++ server/pcapport.cpp | 53 ++++++++++++++++++++++++++++++----------- server/portmanager.cpp | 17 +++++++++---- server/winpcapport.cpp | 3 ++- 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index d0e8c0c..63db109 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -26,6 +26,7 @@ along with this program. If not, see AbstractPort::AbstractPort(int id, const char *device) { + isUsable_ = true; data_.mutable_port_id()->set_id(id); data_.set_name(device); diff --git a/server/abstractport.h b/server/abstractport.h index e903007..c4f798d 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -47,6 +47,8 @@ public: AbstractPort(int id, const char *device); virtual ~AbstractPort(); + bool isUsable() { return isUsable_; } + virtual void init(); int id() { return data_.port_id().id(); } @@ -86,6 +88,7 @@ public: void resetStats() { epochStats_ = stats_; } protected: + bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index d4d1223..683d462 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -35,6 +35,9 @@ PcapPort::PcapPort(int id, const char *device) transmitter_ = new PortTransmitter(device); capturer_ = new PortCapturer(device); + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + if (!deviceList_) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -106,16 +109,27 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) { int ret; - char errbuf[PCAP_ERRBUF_SIZE]; + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; direction_ = direction; isDirectional_ = true; stats_ = stats; - handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, +_retry: + handle_ = pcap_open_live(device, 64 /* FIXME */, flag, 1000 /* ms */, errbuf); if (handle_ == NULL) - goto _open_error; + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", device); + flag = 0; + goto _retry; + } + else + goto _open_error; + } #ifdef Q_OS_WIN32 // pcap_setdirection() API is not supported in Windows. // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 @@ -149,7 +163,7 @@ _set_direction_error: return; _open_error: - qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); } void PcapPort::PortMonitor::run() @@ -201,7 +215,7 @@ void PcapPort::PortMonitor::run() PcapPort::PortTransmitter::PortTransmitter(const char *device) { - char errbuf[PCAP_ERRBUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE] = ""; #ifdef Q_OS_WIN32 LARGE_INTEGER freq; @@ -216,8 +230,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) stop_ = false; stats_ = new AbstractPort::PortStats; usingInternalStats_ = true; - handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, - 1000 /* ms */, errbuf); + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); if (handle_ == NULL) goto _open_error; @@ -227,7 +240,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) return; _open_error: - qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); usingInternalHandle_ = false; } @@ -434,7 +447,8 @@ PcapPort::PortCapturer::~PortCapturer() void PcapPort::PortCapturer::run() { - char errbuf[PCAP_ERRBUF_SIZE]; + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; qDebug("In %s", __PRETTY_FUNCTION__); @@ -443,14 +457,25 @@ void PcapPort::PortCapturer::run() qWarning("temp cap file is not open"); return; } - +_retry: handle_ = pcap_open_live(device_.toAscii().constData(), 65535, - PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); + flag, 1000 /* ms */, errbuf); + if (handle_ == NULL) { - qDebug("Error opening port %s: %s\n", - device_.toAscii().constData(), pcap_geterr(handle_)); - return; + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } } dumpHandle_ = pcap_dump_open(handle_, diff --git a/server/portmanager.cpp b/server/portmanager.cpp index b4f7572..081d5db 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -43,18 +43,27 @@ PortManager::PortManager() { AbstractPort *port; + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + #ifdef Q_OS_WIN32 port = new WinPcapPort(i, device->name); #else port = new PcapPort(i, device->name); #endif + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + port->init(); portList_.append(port); - - qDebug("%d. %s", i, device->name); - if (device->description) - qDebug(" (%s)\n", device->description); } pcap_freealldevs(deviceList); diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 3f38a15..6cdbce8 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -124,7 +124,8 @@ WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) : PcapPort::PortMonitor(device, direction, stats) { - pcap_setmode(handle(), MODE_STAT); + if (handle()) + pcap_setmode(handle(), MODE_STAT); } void WinPcapPort::PortMonitor::run() From 16d85a338aa0642c86316407f88f0ff30b1c3e83 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 21 Feb 2011 22:17:16 +0530 Subject: [PATCH 120/294] snapshot before 2 pass changes --- client/port.cpp | 3 ++- common/pdml_p.cpp | 2 +- ost.pro | 3 ++- test/main.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ test/test.pro | 13 +++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 test/main.cpp create mode 100644 test/test.pro diff --git a/client/port.cpp b/client/port.cpp index e3c873b..5ed914d 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -21,6 +21,7 @@ along with this program. If not, see #include "fileformat.h" #include "pcapfileformat.h" +#include "pdmlfileformat.h" #include #include @@ -229,7 +230,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error) OstProto::StreamConfigList streams; //if (!fileFormat.openStreams(fileName, streams, error)) - if (!pcapFileFormat.openStreams(fileName, streams, error)) + if (!pdmlFileFormat.openStreams(fileName, streams, error)) goto _fail; if (!append) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index fbc4a0e..e51d7c2 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -354,11 +354,11 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) #if 0 factory_.insert("fake-field-wrapper", new PdmlFakeFieldWrapperProtocol()); -#endif factory_.insert("eth",PdmlEthProtocol::createInstance); factory_.insert("ip",PdmlIp4Protocol::createInstance); factory_.insert("ipv6",PdmlIp6Protocol::createInstance); factory_.insert("tcp",PdmlTcpProtocol::createInstance); +#endif } PdmlReader::~PdmlReader() diff --git a/ost.pro b/ost.pro index 0f9d987..c5d4369 100644 --- a/ost.pro +++ b/ost.pro @@ -5,4 +5,5 @@ SUBDIRS = \ rpc/pbrpc.pro \ common/ostproto.pro \ server/drone.pro \ - client/ostinato.pro + client/ostinato.pro \ + test/test.pro diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..0b98cfd --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,42 @@ + +#include "pcapfileformat.h" +#include "pdmlfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" + +#include +#include + +extern ProtocolManager *OstProtocolManager; + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + QString error; + + if (argc != 3) + { + printf("%s \n", argv[0]); + exit(255); + } + + OstProtocolManager = new ProtocolManager(); + + OstProto::StreamConfigList streams; + QString inFile(argv[1]); + QString outFile(argv[2]); + + if (!pdmlFileFormat.openStreams(inFile, streams, error)) + { + fprintf(stdout, "failed reading streams from %s\n", inFile.toAscii().constData()); + exit(1); + } + + if (!pcapFileFormat.saveStreams(streams, outFile, error)) + { + fprintf(stdout, "failed writing streams to %s\n", outFile.toAscii().constData()); + exit(1); + } + + return 0; +} diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..2875525 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" +LIBS += -L"../common/debug" -lostproto +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) From b7ce0a6faf77616ad1d875250aafbbfb56d143cc Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 23 Feb 2011 21:28:32 +0530 Subject: [PATCH 121/294] snapshot --- common/pdml_p.cpp | 175 +++++++++++++++++++++++++++++++++++++--- common/pdml_p.h | 32 ++++++++ server/abstractport.cpp | 1 + server/abstractport.h | 3 + server/pcapport.cpp | 53 ++++++++---- server/portmanager.cpp | 17 +++- server/winpcapport.cpp | 3 +- test/test.pro | 2 + 8 files changed, 258 insertions(+), 28 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index e51d7c2..37e7aa2 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -34,6 +34,8 @@ along with this program. If not, see const int kBaseHex = 16; +static PdmlReader *gPdmlReader = NULL; + PdmlDefaultProtocol::PdmlDefaultProtocol() { ostProtoId_ = -1; @@ -346,6 +348,8 @@ bool PdmlParser::fatalError(const QXmlParseException &exception) // ---------------------------------------------------------- // PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { + gPdmlReader = this; + pass_ = 0; streams_ = streams; factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); @@ -370,6 +374,28 @@ bool PdmlReader::read(QIODevice *device) setDevice(device); packetCount_ = 0; + // 1st pass - preprocessing fake fields + pass_ = 1; + qDebug("PASS %d\n", pass_); + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + clear(); + device->seek(0); + setDevice(device); + + // 2nd pass - actual processing + pass_ = 2; + qDebug("PASS %d\n", pass_); while (!atEnd()) { readNext(); @@ -451,7 +477,7 @@ void PdmlReader::readPdml() { Q_ASSERT(isStartElement() && name() == "pdml"); - packetCount_ = 0; + packetCount_ = 1; while (!atEnd()) { @@ -462,19 +488,137 @@ void PdmlReader::readPdml() if (isStartElement()) { if (name() == "packet") - readPacket(); + { + if (pass_ == 1) + readPacketPass1(); + else if (pass_ == 2) + readPacket(); + else + Q_ASSERT(false); // unreachable! + } else readUnexpectedElement(); } } } +void PdmlReader::readPacketPass1() +{ + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("Pass1 packetNum = %d", packetCount_); + + Fragment f; + f.pos = -1; + f.size = -1; + f.value = QByteArray(); + + pktFragments_.append(f); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + readProtoPass1(); + else if (name() == "field") + skipElement(); + else + readUnexpectedElement(); + } + } + packetCount_++; +} + +void PdmlReader::readProtoPass1() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + if (attributes().value("name") != "fake-field-wrapper") + { + skipElement(); + return; + } + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + readProtoPass1(); + else if (name() == "field") + readFieldPass1(); + else + readUnexpectedElement(); + } + } +} + +void PdmlReader::readFieldPass1() +{ + Q_ASSERT(isStartElement() && name() == "field"); + + if (attributes().value("name") != "data.data") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + QString valueHexStr = attributes().value("value").toString(); + int pos = -1; + int size = -1; + + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("\tFAKE FIELD fieldName:%s, pos:%d, size:%d value:%s", + fieldName.toAscii().constData(), + pos, + size, + valueHexStr.toAscii().constData()); + + pktFragments_[packetCount_-1].pos = pos; + pktFragments_[packetCount_-1].size = size; + pktFragments_[packetCount_-1].value = + QByteArray::fromHex(valueHexStr.toUtf8()); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + readProtoPass1(); + else if (name() == "field") + readFieldPass1(); + else + readUnexpectedElement(); + } + } +} + void PdmlReader::readPacket() { - Q_ASSERT(isStartElement() && name() == "packet"); + //Q_ASSERT(isStartElement() && name() == "packet"); - packetCount_++; - qDebug("packetNum = %d", packetCount_); + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); // XXX: For now, each packet is converted to a stream currentStream_ = streams_->add_stream(); @@ -512,8 +656,9 @@ void PdmlReader::readPacket() hexDump->set_pad_until_end(false); currentStream_->mutable_core()->set_name(""); } -} + packetCount_++; +} void PdmlReader::readProto() { @@ -805,10 +950,22 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, // Skipped field? Pad with zero! if ((pos > expPos_) && (expPos_ < endPos_)) { - QByteArray hexVal(pos - expPos_, char(0)); + PdmlReader::Fragment f; - hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); - expPos_ += hexVal.size(); + f = gPdmlReader->pktFragments_.value(stream->stream_id().id()-1); + + if (expPos_ == f.pos) + { + hexDump->mutable_content()->append(f.value.constData(), f.size); + expPos_ += f.size; + } + else + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } } if ((pos == expPos_) /*&& (pos < endPos_)*/) diff --git a/common/pdml_p.h b/common/pdml_p.h index 174442c..bc07f16 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -90,8 +90,10 @@ private: }; #endif +class PdmlUnknownProtocol; class PdmlReader : public QXmlStreamReader { + friend class PdmlUnknownProtocol; public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); @@ -106,18 +108,48 @@ private: void readPdml(); void skipElement(); void readUnexpectedElement(); + + void readPacketPass1(); + void readProtoPass1(); + void readFieldPass1(); + void readPacket(); void readProto(); void readField(PdmlDefaultProtocol *pdmlProto, google::protobuf::Message *pbProto); typedef PdmlDefaultProtocol* (*FactoryMethod)(); + +#if 0 + class PacketFragment // TODO: find a better name! + { + public: + private: + typedef struct + { + int pos; + int size; + QByteArray value; + } Fragment; + QList + }; +#endif + typedef struct + { + int pos; + int size; + QByteArray value; + } Fragment; + QMap factory_; OstProto::StreamConfigList *streams_; + int pass_; int packetCount_; OstProto::Stream *currentStream_; + QList pktFragments_; + //PdmlDefaultProtocol *currentPdmlProtocol_; //google::protobuf::Message *currentProtocolMsg_; }; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index d0e8c0c..63db109 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -26,6 +26,7 @@ along with this program. If not, see AbstractPort::AbstractPort(int id, const char *device) { + isUsable_ = true; data_.mutable_port_id()->set_id(id); data_.set_name(device); diff --git a/server/abstractport.h b/server/abstractport.h index e903007..c4f798d 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -47,6 +47,8 @@ public: AbstractPort(int id, const char *device); virtual ~AbstractPort(); + bool isUsable() { return isUsable_; } + virtual void init(); int id() { return data_.port_id().id(); } @@ -86,6 +88,7 @@ public: void resetStats() { epochStats_ = stats_; } protected: + bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 0c283c0..fa2a11e 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -35,6 +35,9 @@ PcapPort::PcapPort(int id, const char *device) transmitter_ = new PortTransmitter(device); capturer_ = new PortCapturer(device); + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + if (!deviceList_) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -106,16 +109,27 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) { int ret; - char errbuf[PCAP_ERRBUF_SIZE]; + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; direction_ = direction; isDirectional_ = true; stats_ = stats; - handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, +_retry: + handle_ = pcap_open_live(device, 64 /* FIXME */, flag, 1000 /* ms */, errbuf); if (handle_ == NULL) - goto _open_error; + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", device); + flag = 0; + goto _retry; + } + else + goto _open_error; + } #ifdef Q_OS_WIN32 // pcap_setdirection() API is not supported in Windows. // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 @@ -149,7 +163,7 @@ _set_direction_error: return; _open_error: - qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); } void PcapPort::PortMonitor::run() @@ -201,7 +215,7 @@ void PcapPort::PortMonitor::run() PcapPort::PortTransmitter::PortTransmitter(const char *device) { - char errbuf[PCAP_ERRBUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE] = ""; #ifdef Q_OS_WIN32 LARGE_INTEGER freq; @@ -216,8 +230,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) stop_ = false; stats_ = new AbstractPort::PortStats; usingInternalStats_ = true; - handle_ = pcap_open_live(device, 64 /* FIXME */, PCAP_OPENFLAG_PROMISCUOUS, - 1000 /* ms */, errbuf); + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); if (handle_ == NULL) goto _open_error; @@ -227,7 +240,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) return; _open_error: - qDebug("Error opening port %s: %s\n", device, pcap_geterr(handle_)); + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); usingInternalHandle_ = false; } @@ -434,7 +447,8 @@ PcapPort::PortCapturer::~PortCapturer() void PcapPort::PortCapturer::run() { - char errbuf[PCAP_ERRBUF_SIZE]; + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; qDebug("In %s", __PRETTY_FUNCTION__); @@ -443,14 +457,25 @@ void PcapPort::PortCapturer::run() qWarning("temp cap file is not open"); return; } - +_retry: handle_ = pcap_open_live(device_.toAscii().constData(), 65535, - PCAP_OPENFLAG_PROMISCUOUS, 1000 /* ms */, errbuf); + flag, 1000 /* ms */, errbuf); + if (handle_ == NULL) { - qDebug("Error opening port %s: %s\n", - device_.toAscii().constData(), pcap_geterr(handle_)); - return; + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } } dumpHandle_ = pcap_dump_open(handle_, diff --git a/server/portmanager.cpp b/server/portmanager.cpp index b4f7572..081d5db 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -43,18 +43,27 @@ PortManager::PortManager() { AbstractPort *port; + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + #ifdef Q_OS_WIN32 port = new WinPcapPort(i, device->name); #else port = new PcapPort(i, device->name); #endif + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + port->init(); portList_.append(port); - - qDebug("%d. %s", i, device->name); - if (device->description) - qDebug(" (%s)\n", device->description); } pcap_freealldevs(deviceList); diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 3f38a15..6cdbce8 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -124,7 +124,8 @@ WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) : PcapPort::PortMonitor(device, direction, stats) { - pcap_setmode(handle(), MODE_STAT); + if (handle()) + pcap_setmode(handle(), MODE_STAT); } void WinPcapPort::PortMonitor::run() diff --git a/test/test.pro b/test/test.pro index 2875525..8e055de 100644 --- a/test/test.pro +++ b/test/test.pro @@ -5,6 +5,8 @@ INCLUDEPATH += "../rpc/" "../common/" LIBS += -L"../common/debug" -lostproto LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +POST_TARGETDEPS += "../common/debug/libostproto.a" + HEADERS += SOURCES += main.cpp From 24dc62114548cde7d1746dd2796462bf46807c60 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 2 Mar 2011 22:21:23 +0530 Subject: [PATCH 122/294] Added "non promiscuous mode" to port notes as a limitation --- server/pcapport.cpp | 19 +++++++++++-------- server/pcapport.h | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 683d462..ebb159a 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -90,18 +90,21 @@ void PcapPort::updateNotes() { QString notes; + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) - notes.append("Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
    "); + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); if (!monitorTx_->isDirectional() && !hasExclusiveControl()) - notes.append("Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
    "); + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); if (notes.isEmpty()) data_.set_notes(""); else data_.set_notes(QString("Limitation(s)" - "

    %1
    " - "Rx/Tx Rates are also subject to above limitation(s)

    "). + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). arg(notes).toStdString()); } @@ -109,22 +112,22 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) { int ret; - int flag = PCAP_OPENFLAG_PROMISCUOUS; char errbuf[PCAP_ERRBUF_SIZE] = ""; direction_ = direction; isDirectional_ = true; + isPromisc_ = true; stats_ = stats; _retry: - handle_ = pcap_open_live(device, 64 /* FIXME */, flag, + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), 1000 /* ms */, errbuf); if (handle_ == NULL) { - if (flag && QString(errbuf).contains("promiscuous")) + if (isPromisc_ && QString(errbuf).contains("promiscuous")) { qDebug("%s:can't set promiscuous mode, trying non-promisc", device); - flag = 0; + isPromisc_ = false; goto _retry; } else diff --git a/server/pcapport.h b/server/pcapport.h index a6264c5..e82589b 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -78,12 +78,14 @@ protected: pcap_t* handle() { return handle_; } Direction direction() { return direction_; } bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } protected: AbstractPort::PortStats *stats_; private: pcap_t *handle_; Direction direction_; bool isDirectional_; + bool isPromisc_; }; class PortTransmitter: public QThread From 5af591b96b371bb297aef8df91fdeece09940aca Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Mar 2011 18:41:08 +0530 Subject: [PATCH 123/294] Snapshot before next improvement changes --- client/port.cpp | 3 +- common/pcapfileformat.cpp | 237 ++++++++++++++++++++++++++++---------- common/pcapfileformat.h | 28 ++++- common/pdml_p.cpp | 141 +++++++++++++++++++++-- common/pdml_p.h | 8 +- test/main.cpp | 8 +- 6 files changed, 347 insertions(+), 78 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 5ed914d..8d99cfd 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -230,7 +230,8 @@ bool Port::openStreams(QString fileName, bool append, QString &error) OstProto::StreamConfigList streams; //if (!fileFormat.openStreams(fileName, streams, error)) - if (!pdmlFileFormat.openStreams(fileName, streams, error)) + //if (!pdmlFileFormat.openStreams(fileName, streams, error)) + if (!pcapFileFormat.openStreams(fileName, streams, error)) goto _fail; if (!append) diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 3ef8c76..24f1df9 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2011 Srivats P. This file is part of "Ostinato" @@ -18,11 +18,15 @@ along with this program. If not, see */ #include "pcapfileformat.h" + +#include "pdml_p.h" #include "streambase.h" #include "hexdump.pb.h" #include #include +#include +#include #include static inline quint32 swap32(quint32 val) @@ -39,28 +43,12 @@ static inline quint16 swap16(quint16 val) ((val << 8) && 0xFF00)); } -typedef struct { - quint32 magicNumber; /* magic number */ - quint16 versionMajor; /* major version number */ - quint16 versionMinor; /* minor version number */ - qint32 thisZone; /* GMT to local correction */ - quint32 sigfigs; /* accuracy of timestamps */ - quint32 snapLen; /* max length of captured packets, in octets */ - quint32 network; /* data link type */ -} PcapFileHeader; - const quint32 kPcapFileMagic = 0xa1b2c3d4; const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; const quint16 kPcapFileVersionMajor = 2; const quint16 kPcapFileVersionMinor = 4; const quint32 kMaxSnapLen = 65535; - -typedef struct { - quint32 tsSec; /* timestamp seconds */ - quint32 tsUsec; /* timestamp microseconds */ - quint32 inclLen; /* number of octets of packet saved in file */ - quint32 origLen; /* actual length of packet */ -} PcapPacketHeader; +const quint32 kDltEthernet = 1; PcapFileFormat pcapFileFormat; @@ -75,54 +63,140 @@ PcapFileFormat::~PcapFileFormat() bool PcapFileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { + bool viaPdml = true; // TODO: shd be a param to function + bool isOk = false; + int pktCount; QFile file(fileName); - QDataStream fd; + QTemporaryFile file2; quint32 magic; + uchar gzipMagic[2]; + int len; PcapFileHeader fileHdr; PcapPacketHeader pktHdr; - quint32 len; - int pktCount = 1; QByteArray pktBuf; if (!file.open(QIODevice::ReadOnly)) goto _err_open; - if (file.size() < sizeof(fileHdr)) - goto _err_truncated; + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; - fd.setDevice(&file); + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; - fd >> magic; + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + // FIXME: hardcoded prog name + gzip.start("C:/Program Files/CmdLineTools/gzip.exe", + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + fd_ >> magic; + + qDebug("magic = %08x", magic); if (magic == kPcapFileMagicSwapped) { // Toggle Byte order - if (fd.byteOrder() == QDataStream::BigEndian) - fd.setByteOrder(QDataStream::LittleEndian); + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); else - fd.setByteOrder(QDataStream::BigEndian); + fd_.setByteOrder(QDataStream::BigEndian); } else if (magic != kPcapFileMagic) goto _err_bad_magic; - fd >> fileHdr.versionMajor; - fd >> fileHdr.versionMinor; - fd >> fileHdr.thisZone; - fd >> fileHdr.sigfigs; - fd >> fileHdr.snapLen; - fd >> fileHdr.network; + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; if ((fileHdr.versionMajor != kPcapFileVersionMajor) || (fileHdr.versionMinor != kPcapFileVersionMinor)) goto _err_unsupported_version; - // TODO: what do we do about non-ethernet networks? +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif pktBuf.resize(fileHdr.snapLen); - while (!fd.atEnd()) + if (viaPdml) + { + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + QProcess tshark; + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + // FIXME: hardcoded prog name + tshark.start("C:/Program Files/Wireshark/Tshark.exe", + QStringList() + << QString("-r%1").arg(fileName) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + isOk = reader.read(&pdmlFile, this); // TODO: pass error string? + goto _exit; + + } + +_non_pdml: + pktCount = 1; + while (!fd_.atEnd()) { OstProto::Stream *stream = streams.add_stream(); OstProto::Protocol *proto = stream->add_protocol(); @@ -133,21 +207,11 @@ bool PcapFileFormat::openStreams(const QString fileName, proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); - // read PcapPacketHeader - fd >> pktHdr.tsSec; - fd >> pktHdr.tsUsec; - fd >> pktHdr.inclLen; - fd >> pktHdr.origLen; - - // TODO: chk fd.status() + readPacket(pktHdr, pktBuf); // validations on inclLen <= origLen && inclLen <= snapLen Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if - // read Pkt contents - len = fd.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? - Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert - hexDump->set_content(pktBuf.data(), pktHdr.inclLen); hexDump->set_pad_until_end(false); @@ -159,6 +223,14 @@ bool PcapFileFormat::openStreams(const QString fileName, isOk = true; goto _exit; +#if 1 +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.network); + goto _exit; +#endif + _err_unsupported_version: error = QString(tr("%1 is in PCAP version %2.%3 format which is " "not supported - Sorry!")) @@ -169,24 +241,65 @@ _err_bad_magic: error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); goto _exit; +#if 0 _err_truncated: error = QString(tr("%1 is too short")).arg(fileName); goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; _err_open: error = QString(tr("Unable to open file: %1")).arg(fileName); goto _exit; _exit: + file.close(); return isOk; } +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { bool isOk = false; QFile file(fileName); - QDataStream fd; PcapFileHeader fileHdr; PcapPacketHeader pktHdr; QByteArray pktBuf; @@ -194,7 +307,7 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, if (!file.open(QIODevice::WriteOnly)) goto _err_open; - fd.setDevice(&file); + fd_.setDevice(&file); fileHdr.magicNumber = kPcapFileMagic; fileHdr.versionMajor = kPcapFileVersionMajor; @@ -202,15 +315,15 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, fileHdr.thisZone = 0; fileHdr.sigfigs = 0; fileHdr.snapLen = kMaxSnapLen; - fileHdr.network = 1; // Ethernet; FIXME: Hardcoding + fileHdr.network = kDltEthernet; - fd << fileHdr.magicNumber; - fd << fileHdr.versionMajor; - fd << fileHdr.versionMinor; - fd << fileHdr.thisZone; - fd << fileHdr.sigfigs; - fd << fileHdr.snapLen; - fd << fileHdr.network; + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; pktBuf.resize(kMaxSnapLen); @@ -230,11 +343,11 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, if (pktHdr.inclLen > fileHdr.snapLen) pktHdr.inclLen = fileHdr.snapLen; - fd << pktHdr.tsSec; - fd << pktHdr.tsUsec; - fd << pktHdr.inclLen; - fd << pktHdr.origLen; - fd.writeRawData(pktBuf.data(), pktHdr.inclLen); + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); } file.close(); diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h index a515e32..04a02ec 100644 --- a/common/pcapfileformat.h +++ b/common/pcapfileformat.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2011 Srivats P. This file is part of "Ostinato" @@ -21,12 +21,16 @@ along with this program. If not, see #include "protocol.pb.h" +#include #include +class PdmlReader; class PcapFileFormat : public QObject { Q_OBJECT + friend class PdmlReader; + public: PcapFileFormat(); ~PcapFileFormat(); @@ -35,6 +39,28 @@ public: OstProto::StreamConfigList &streams, QString &error); bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; }; extern PcapFileFormat pcapFileFormat; diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 37e7aa2..d681667 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -19,8 +19,10 @@ along with this program. If not, see #include "pdml_p.h" +#include "PcapFileFormat.h" #include "mac.pb.h" #include "eth2.pb.h" +#include "dot3.pb.h" #include "hexdump.pb.h" #include "ip4.pb.h" #include "ip6.pb.h" @@ -349,6 +351,7 @@ bool PdmlParser::fatalError(const QXmlParseException &exception) PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { gPdmlReader = this; + pcap_ = NULL; pass_ = 0; streams_ = streams; @@ -358,20 +361,21 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) #if 0 factory_.insert("fake-field-wrapper", new PdmlFakeFieldWrapperProtocol()); +#endif factory_.insert("eth",PdmlEthProtocol::createInstance); factory_.insert("ip",PdmlIp4Protocol::createInstance); factory_.insert("ipv6",PdmlIp6Protocol::createInstance); factory_.insert("tcp",PdmlTcpProtocol::createInstance); -#endif } PdmlReader::~PdmlReader() { } -bool PdmlReader::read(QIODevice *device) +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) { setDevice(device); + pcap_ = pcap; packetCount_ = 0; // 1st pass - preprocessing fake fields @@ -408,7 +412,14 @@ bool PdmlReader::read(QIODevice *device) } } - return !error(); + if (error()) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); // FIXME + return false; + } + return true; } // TODO: use a temp pool to avoid a lot of new/delete @@ -502,6 +513,8 @@ void PdmlReader::readPdml() } } +/////////////////////// PASS 1 ////////////////////////// + void PdmlReader::readPacketPass1() { Q_ASSERT(isStartElement() && name() == "packet"); @@ -614,17 +627,24 @@ void PdmlReader::readFieldPass1() } } +/////////////////////// PASS 2 ////////////////////////// + void PdmlReader::readPacket() { - //Q_ASSERT(isStartElement() && name() == "packet"); + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); - // XXX: For now, each packet is converted to a stream + // XXX: we play dumb and convert each packet to a stream, for now currentStream_ = streams_->add_stream(); currentStream_->mutable_stream_id()->set_id(packetCount_); currentStream_->mutable_core()->set_is_enabled(true); + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + while (!atEnd()) { readNext(); @@ -668,9 +688,23 @@ void PdmlReader::readProto() Q_ASSERT(isStartElement() && name() == "proto"); QString protoName = attributes().value("name").toString(); - qDebug("proto: %s", protoName.toAscii().constData()); + int pos = -1; -#if 0 + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + + qDebug("proto: %s, pos = %d", protoName.toAscii().constData(), pos); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + if ((pos == 0) && (currentStream_->protocol_size() > 0)) + { + qDebug("(skipped)"); + skipElement(); + return; + } + +#if 1 if (protoName.isEmpty() || (protoName == "expert")) { skipElement(); @@ -678,6 +712,34 @@ void PdmlReader::readProto() } #endif + if (!factory_.contains(protoName) && pcap_) + { + int pos = -1; + int size = -1; + + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + hexDump->set_content(pktBuf_.constData() + pos, size); + hexDump->set_pad_until_end(false); + + skipElement(); + return; + } + } + pdmlProto = allocPdmlProtocol(protoName); Q_ASSERT(pdmlProto != NULL); @@ -902,6 +964,11 @@ void PdmlUnknownProtocol::preProtocolHandler(QString name, if (!isOk) goto _skip_pos_size_proc; + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if (quint32(pos + size) > stream->core().frame_len()) + goto _skip_pos_size_proc; + expPos_ = pos; endPos_ = expPos_ + size; @@ -1048,6 +1115,8 @@ void PdmlFakeFieldWrapperProtocol::postProtocolHandler(OstProto::Stream *stream) qDebug("%s: expPos_ = %d\n", __FUNCTION__, expPos_); + // TODO: if content size is zero, remove protocol? + hexDump->set_pad_until_end(false); expPos_ = -1; } @@ -1059,6 +1128,7 @@ void PdmlFakeFieldWrapperProtocol::unknownFieldHandler(QString name, int pos, stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); if ((pos == expPos_) && (size >= 0) && + (!name.startsWith("tcp.segment")) && (!attributes.value("unmaskedvalue").isEmpty() || !attributes.value("value").isEmpty())) { @@ -1106,6 +1176,36 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, eth2->set_type(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); eth2->set_is_override_type(true); } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } } @@ -1141,7 +1241,13 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, { bool isOk; - if (name == "ip.flags") + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") { OstProto::Ip4 *ip4 = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::ip4); @@ -1160,6 +1266,21 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Stream *stream) ip4->set_is_override_totlen(true); // FIXME ip4->set_is_override_proto(true); // FIXME ip4->set_is_override_cksum(true); // FIXME + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } } // ---------------------------------------------------------- // @@ -1255,8 +1376,8 @@ void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, && attributes.value("show").toString().startsWith("TCP segment data")) { segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - stream->mutable_core()->mutable_name()->append(segmentData_.constData(), - segmentData_.size()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); } } diff --git a/common/pdml_p.h b/common/pdml_p.h index bc07f16..242d3eb 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -91,6 +91,7 @@ private: #endif class PdmlUnknownProtocol; +class PcapFileFormat; class PdmlReader : public QXmlStreamReader { friend class PdmlUnknownProtocol; @@ -98,7 +99,7 @@ public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); - bool read(QIODevice *device); + bool read(QIODevice *device, PcapFileFormat *pcap = NULL); private: PdmlDefaultProtocol* allocPdmlProtocol(QString protoName); @@ -144,12 +145,15 @@ private: QMap factory_; OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; int pass_; int packetCount_; OstProto::Stream *currentStream_; QList pktFragments_; + QByteArray pktBuf_; + //PdmlDefaultProtocol *currentPdmlProtocol_; //google::protobuf::Message *currentProtocolMsg_; }; @@ -230,6 +234,8 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); +private: + QByteArray options_; }; class PdmlIp6Protocol : public PdmlDefaultProtocol diff --git a/test/main.cpp b/test/main.cpp index 0b98cfd..c69ccbd 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -26,15 +26,17 @@ int main(int argc, char* argv[]) QString inFile(argv[1]); QString outFile(argv[2]); - if (!pdmlFileFormat.openStreams(inFile, streams, error)) + if (!pcapFileFormat.openStreams(inFile, streams, error)) { - fprintf(stdout, "failed reading streams from %s\n", inFile.toAscii().constData()); + fprintf(stdout, "failed reading streams from %s:%s\n", + inFile.toAscii().constData(), error.toAscii().constData()); exit(1); } if (!pcapFileFormat.saveStreams(streams, outFile, error)) { - fprintf(stdout, "failed writing streams to %s\n", outFile.toAscii().constData()); + fprintf(stdout, "failed writing streams to %s:%s\n", + outFile.toAscii().constData(), error.toAscii().constData()); exit(1); } From ed03f06f442a4b6b6117a621cfc34559fbe6b305 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 12 Mar 2011 15:16:27 +0530 Subject: [PATCH 124/294] pcap open via pdml is now working. cleanup required --- common/pcapfileformat.cpp | 42 ++++++-- common/pdml_p.cpp | 219 ++++++++++++++++++++++++++++++++++---- common/pdml_p.h | 17 ++- 3 files changed, 244 insertions(+), 34 deletions(-) diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 24f1df9..967c565 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -66,7 +66,6 @@ bool PcapFileFormat::openStreams(const QString fileName, bool viaPdml = true; // TODO: shd be a param to function bool isOk = false; - int pktCount; QFile file(fileName); QTemporaryFile file2; quint32 magic; @@ -74,6 +73,9 @@ bool PcapFileFormat::openStreams(const QString fileName, int len; PcapFileHeader fileHdr; PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; QByteArray pktBuf; if (!file.open(QIODevice::ReadOnly)) @@ -176,6 +178,7 @@ bool PcapFileFormat::openStreams(const QString fileName, tshark.start("C:/Program Files/Wireshark/Tshark.exe", QStringList() << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" << "-Tpdml"); if (!tshark.waitForStarted(-1)) { @@ -202,8 +205,6 @@ _non_pdml: OstProto::Protocol *proto = stream->add_protocol(); OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - stream->mutable_stream_id()->set_id(pktCount); - stream->mutable_core()->set_is_enabled(true); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); @@ -215,8 +216,23 @@ _non_pdml: hexDump->set_content(pktBuf.data(), pktHdr.inclLen); hexDump->set_pad_until_end(false); + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; pktCount++; } @@ -327,6 +343,8 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, pktBuf.resize(kMaxSnapLen); + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; for (int i = 0; i < streams.stream_size(); i++) { StreamBase s; @@ -336,10 +354,12 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, // TODO: expand frameIndex for each stream s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); - // TODO: write actual timing!?!? - pktHdr.tsSec = 0; - pktHdr.tsUsec = 0; - pktHdr.inclLen = pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + if (pktHdr.inclLen > fileHdr.snapLen) pktHdr.inclLen = fileHdr.snapLen; @@ -348,6 +368,14 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, fd_ << pktHdr.inclLen; fd_ << pktHdr.origLen; fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += 1000000/s.packetRate(); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } } file.close(); diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index d681667..bb8e54d 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -19,7 +19,11 @@ along with this program. If not, see #include "pdml_p.h" -#include "PcapFileFormat.h" +#include "abstractprotocol.h" +#include "pcapfileformat.h" +#include "protocolmanager.h" +#include "streambase.h" + #include "mac.pb.h" #include "eth2.pb.h" #include "dot3.pb.h" @@ -34,6 +38,8 @@ along with this program. If not, see #include +extern ProtocolManager *OstProtocolManager; + const int kBaseHex = 16; static PdmlReader *gPdmlReader = NULL; @@ -72,8 +78,9 @@ int PdmlDefaultProtocol::fieldId(QString name) const return fieldMap_.value(name); } -void PdmlDefaultProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) +void PdmlDefaultProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Stream* /*stream*/) { return; // do nothing! } @@ -355,6 +362,9 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) pass_ = 0; streams_ = streams; + currentStream_ = NULL; + prevStream_ = NULL; + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); @@ -378,6 +388,7 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) pcap_ = pcap; packetCount_ = 0; +#if 0 // 1st pass - preprocessing fake fields pass_ = 1; qDebug("PASS %d\n", pass_); @@ -397,6 +408,7 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) device->seek(0); setDevice(device); +#endif // 2nd pass - actual processing pass_ = 2; qDebug("PASS %d\n", pass_); @@ -637,11 +649,19 @@ void PdmlReader::readPacket() qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + skipUntilEnd_ = false; + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; currentStream_ = streams_->add_stream(); currentStream_->mutable_stream_id()->set_id(packetCount_); currentStream_->mutable_core()->set_is_enabled(true); + // Set to a high number; will get reset to correct during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + if (pcap_) pcap_->readPacket(pktHdr, pktBuf_); @@ -654,7 +674,9 @@ void PdmlReader::readPacket() if (isStartElement()) { - if (name() == "proto") + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") readProto(); else if (name() == "field") readField(NULL, NULL); // TODO: top level field!!!! @@ -662,8 +684,9 @@ void PdmlReader::readPacket() readUnexpectedElement(); } } - + // BAD Hack for TCP Segments if (currentStream_->core().name().size()) +#if 0 { OstProto::Protocol *proto = currentStream_->add_protocol(); @@ -672,12 +695,39 @@ void PdmlReader::readPacket() OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + qDebug("Adding TCP Segment Data/FCS etc of size %d", + currentStream_->core().name().size()); + hexDump->set_content(currentStream_->core().name()); hexDump->set_pad_until_end(false); currentStream_->mutable_core()->set_name(""); + + expPos_ += hexDump->content().size(); } +#else + currentStream_->mutable_core()->set_name(""); +#endif + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } packetCount_++; + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); } void PdmlReader::readProto() @@ -689,15 +739,22 @@ void PdmlReader::readProto() QString protoName = attributes().value("name").toString(); int pos = -1; + int size = -1; if (!attributes().value("pos").isEmpty()) pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); - qDebug("proto: %s, pos = %d", protoName.toAscii().constData(), pos); + qDebug("proto: %s, pos = %d, expPos_ = %d", + protoName.toAscii().constData(), pos, expPos_); // This is a heuristic to skip protocols which are not part of // this frame, but of a reassembled segment spanning several frames - if ((pos == 0) && (currentStream_->protocol_size() > 0)) + // 1. Proto starting pos is 0, but we already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) { qDebug("(skipped)"); skipElement(); @@ -712,16 +769,45 @@ void PdmlReader::readProto() } #endif + // detect overlaps or gaps between subsequent protocols and "fill-in" + // with a "hexdump" from the pcap + if (pos >=0 && pcap_) + { + if (pos > expPos_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", + pos - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, pos - expPos_); + hexDump->set_pad_until_end(false); + + expPos_ = pos; + } + } + + // for unknown protocol, read a hexdump from the pcap if (!factory_.contains(protoName) && pcap_) { - int pos = -1; int size = -1; - if (!attributes().value("pos").isEmpty()) - pos = attributes().value("pos").toString().toInt(); if (!attributes().value("size").isEmpty()) size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + if (pos >= 0 && size > 0 && ((pos + size) <= pktBuf_.size())) { @@ -732,10 +818,14 @@ void PdmlReader::readProto() proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); + qDebug("missing bytes - filling in %d bytes staring from %d", + size, pos); hexDump->set_content(pktBuf_.constData() + pos, size); hexDump->set_pad_until_end(false); skipElement(); + + expPos_ += size; return; } } @@ -762,7 +852,8 @@ void PdmlReader::readProto() } - pdmlProto->preProtocolHandler(protoName, attributes(), currentStream_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, + currentStream_); while (!atEnd()) { @@ -794,6 +885,10 @@ void PdmlReader::readProto() { pdmlProto->prematureEndHandler(endPos, currentStream_); pdmlProto->postProtocolHandler(currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); } readProto(); pdmlProto = NULL; @@ -801,6 +896,17 @@ void PdmlReader::readProto() } else if (name() == "field") { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) { pdmlProto = allocPdmlProtocol(protoName); @@ -822,7 +928,8 @@ void PdmlReader::readProto() pbProto = msgRefl->MutableMessage(proto, fieldDesc); } - pdmlProto->preProtocolHandler(protoName, attributes(), currentStream_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, currentStream_); } readField(pdmlProto, pbProto); } @@ -835,6 +942,10 @@ void PdmlReader::readProto() { pdmlProto->postProtocolHandler(currentStream_); freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); } } @@ -952,13 +1063,19 @@ PdmlDefaultProtocol* PdmlUnknownProtocol::createInstance() } void PdmlUnknownProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Stream *stream) { bool isOk; int size; int pos = attributes.value("pos").toString().toUInt(&isOk); if (!isOk) - goto _skip_pos_size_proc; + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } size = attributes.value("size").toString().toUInt(&isOk); if (!isOk) @@ -966,7 +1083,7 @@ void PdmlUnknownProtocol::preProtocolHandler(QString name, // If pos+size goes beyond the frame length, this is a "reassembled" // protocol and should be skipped - if (quint32(pos + size) > stream->core().frame_len()) + if ((pos + size) > int(stream->core().frame_len())) goto _skip_pos_size_proc; expPos_ = pos; @@ -1001,6 +1118,11 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) //Q_ASSERT(expPos_ == endPos_); hexDump->set_pad_until_end(false); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + endPos_ = expPos_ = -1; } @@ -1017,6 +1139,7 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, // Skipped field? Pad with zero! if ((pos > expPos_) && (expPos_ < endPos_)) { +#if 0 PdmlReader::Fragment f; f = gPdmlReader->pktFragments_.value(stream->stream_id().id()-1); @@ -1027,6 +1150,7 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, expPos_ += f.size; } else +#endif { QByteArray hexVal(pos - expPos_, char(0)); @@ -1061,11 +1185,14 @@ PdmlDefaultProtocol* PdmlGenInfoProtocol::createInstance() return new PdmlGenInfoProtocol(); } +#if 0 // done in frame proto void PdmlGenInfoProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) { - stream->mutable_core()->set_frame_len(size+4); // TODO:check FCS + if (name == "len") + stream->mutable_core()->set_frame_len(size+4); // TODO:check FCS } +#endif // ---------------------------------------------------------- // // PdmlFrameProtocol // @@ -1081,6 +1208,43 @@ PdmlDefaultProtocol* PdmlFrameProtocol::createInstance() return new PdmlFrameProtocol(); } +void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, + int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} + #if 1 // ---------------------------------------------------------- // // PdmlFakeFieldWrapperProtocol // @@ -1100,7 +1264,8 @@ PdmlDefaultProtocol* PdmlFakeFieldWrapperProtocol::createInstance() } void PdmlFakeFieldWrapperProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Stream *stream) { expPos_ = 0; OstProto::HexDump *hexDump = stream->mutable_protocol( @@ -1372,12 +1537,22 @@ void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, { if (name == "tcp.options") options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - else if (name == "" - && attributes.value("show").toString().startsWith("TCP segment data")) + else if (name == "") { - segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - stream->mutable_core()->mutable_name()->insert(0, - segmentData_.constData(), segmentData_.size()); + if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } + else if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } } } diff --git a/common/pdml_p.h b/common/pdml_p.h index 242d3eb..ab512eb 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -47,7 +47,8 @@ public: int fieldId(QString name) const; virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -149,6 +150,9 @@ private: int pass_; int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; OstProto::Stream *currentStream_; QList pktFragments_; @@ -166,7 +170,8 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -183,8 +188,6 @@ public: static PdmlDefaultProtocol* createInstance(); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); }; class PdmlFrameProtocol : public PdmlDefaultProtocol @@ -193,6 +196,9 @@ public: PdmlFrameProtocol(); static PdmlDefaultProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Stream *stream); }; #if 1 @@ -204,7 +210,8 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream); From 69d488d8a23978d895509c58aece1292f499a51f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 13 Mar 2011 22:36:33 +0530 Subject: [PATCH 125/294] Refactored FileFormat Classes with an Abstract base class. Open/Save streams enhanced to support the new file formats - PCAP and PDML --- client/port.cpp | 25 +++++++----- client/port.h | 2 +- client/portswindow.cpp | 38 +++++++++++++++--- common/abstractfileformat.cpp | 73 +++++++++++++++++++++++++++++++++++ common/abstractfileformat.h | 52 +++++++++++++++++++++++++ common/fileformat.cpp | 32 +++++++++++++++ common/fileformat.h | 19 ++++----- common/ostproto.pro | 2 + common/pcapfileformat.cpp | 16 +++++++- common/pcapfileformat.h | 10 ++--- common/pdmlfileformat.cpp | 37 ++++++++++++++++++ common/pdmlfileformat.h | 18 ++++----- 12 files changed, 284 insertions(+), 40 deletions(-) create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h diff --git a/client/port.cpp b/client/port.cpp index 8d99cfd..90992ab 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -19,9 +19,7 @@ along with this program. If not, see #include "port.h" -#include "fileformat.h" -#include "pcapfileformat.h" -#include "pdmlfileformat.h" +#include "abstractfileformat.h" #include #include @@ -228,10 +226,12 @@ void Port::updateStats(OstProto::PortStats *portStats) bool Port::openStreams(QString fileName, bool append, QString &error) { OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); - //if (!fileFormat.openStreams(fileName, streams, error)) - //if (!pdmlFileFormat.openStreams(fileName, streams, error)) - if (!pcapFileFormat.openStreams(fileName, streams, error)) + if (fmt == NULL) + goto _fail; + + if (!fmt->openStreams(fileName, streams, error)) goto _fail; if (!append) @@ -253,10 +253,14 @@ _fail: return false; } -bool Port::saveStreams(QString fileName, QString &error) +bool Port::saveStreams(QString fileName, QString fileType, QString &error) { + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); OstProto::StreamConfigList streams; + if (fmt == NULL) + goto _fail; + streams.mutable_port_id()->set_id(0); for (int i = 0; i < mStreams.size(); i++) { @@ -264,6 +268,9 @@ bool Port::saveStreams(QString fileName, QString &error) mStreams[i]->protoDataCopyInto(*s); } - //return fileFormat.saveStreams(streams, fileName, error); - return pcapFileFormat.saveStreams(streams, fileName, error); + return fmt->saveStreams(streams, fileName, error); + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + return false; } diff --git a/client/port.h b/client/port.h index 2fbb9a0..275ea61 100644 --- a/client/port.h +++ b/client/port.h @@ -119,7 +119,7 @@ public: void updateStats(OstProto::PortStats *portStats); bool openStreams(QString fileName, bool append, QString &error); - bool saveStreams(QString fileName, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); signals: void portDataChanged(int portGroupId, int portId); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 58070a0..86bcf71 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -19,13 +19,14 @@ along with this program. If not, see #include "portswindow.h" +#include "abstractfileformat.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + #include #include #include -#include "streamconfigdialog.h" -#include "streamlistdelegate.h" - PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent) { @@ -497,17 +498,44 @@ void PortsWindow::on_actionSave_Streams_triggered() QModelIndex current = tvPortList->selectionModel()->currentIndex(); QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need. +#ifdef Q_OS_MAC + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); Q_ASSERT(plm->isPort(current)); - fileName = QFileDialog::getSaveFileName(this, tr("Save Streams")); +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); if (fileName.isEmpty()) goto _exit; + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + // TODO: all or selected? - if (!plm->port(current).saveStreams(fileName, errorStr)) + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) QMessageBox::critical(this, qApp->applicationName(), errorStr); else if (!errorStr.isEmpty()) QMessageBox::warning(this, qApp->applicationName(), errorStr); diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..cb3fa43 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,73 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..7a648ee --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class AbstractFileFormat : public QObject +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + + static QStringList supportedFileTypes(); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif +}; + +#endif + diff --git a/common/fileformat.cpp b/common/fileformat.cpp index 619ab37..7ddc65b 100644 --- a/common/fileformat.cpp +++ b/common/fileformat.cpp @@ -394,6 +394,38 @@ _fail: return false; } +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) { // Fill in the "native" file format version diff --git a/common/fileformat.h b/common/fileformat.h index ebdc97c..0204906 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -19,24 +19,27 @@ along with this program. If not, see #ifndef _FILE_FORMAT_H #define _FILE_FORMAT_H +#include "abstractfileformat.h" + #include "fileformat.pb.h" -#include -#include - -class FileFormat : public QObject +class FileFormat : public AbstractFileFormat { - Q_OBJECT public: FileFormat(); ~FileFormat(); - bool openStreams(const QString fileName, + virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); - bool saveStreams(const OstProto::StreamConfigList streams, + virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + private: + void initFileMetaData(OstProto::FileMetaData &metaData); + static const int kFileMagicSize = 12; static const int kFileChecksumSize = 5; static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; @@ -50,8 +53,6 @@ private: static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 1; static const uint kFileFormatVersionRevision = 3; - - void initFileMetaData(OstProto::FileMetaData &metaData); }; extern FileFormat fileFormat; diff --git a/common/ostproto.pro b/common/ostproto.pro index ef829da..3283090 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -57,6 +57,7 @@ PROTOS += \ HEADERS += \ abstractprotocol.h \ comboprotocol.h \ + abstractfileformat.h \ fileformat.h \ pcapfileformat.h \ pdmlfileformat.h \ @@ -98,6 +99,7 @@ HEADERS += \ SOURCES += \ abstractprotocol.cpp \ crc32c.cpp \ + abstractfileformat.cpp \ fileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 967c565..a893565 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -25,6 +25,7 @@ along with this program. If not, see #include #include +#include #include #include #include @@ -243,7 +244,7 @@ _non_pdml: _err_unsupported_encap: error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " "not supported - Sorry!")) - .arg(fileName).arg(fileHdr.network); + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); goto _exit; #endif @@ -391,3 +392,16 @@ _exit: return isOk; } +bool PcapFileFormat::isMyFileFormat(const QString fileName) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h index 04a02ec..4f7ab7a 100644 --- a/common/pcapfileformat.h +++ b/common/pcapfileformat.h @@ -19,16 +19,13 @@ along with this program. If not, see #ifndef _PCAP_FILE_FORMAT_H #define _PCAP_FILE_FORMAT_H -#include "protocol.pb.h" +#include "abstractfileformat.h" #include -#include class PdmlReader; -class PcapFileFormat : public QObject +class PcapFileFormat : public AbstractFileFormat { - Q_OBJECT - friend class PdmlReader; public: @@ -40,6 +37,9 @@ public: bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + private: typedef struct { quint32 magicNumber; /* magic number */ diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 3f86084..0c51652 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -92,6 +92,43 @@ _exit: bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { + error = "Save to PDML format is not supported"; return false; } +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + xml.readNext(); + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h index d392c6b..e05026a 100644 --- a/common/pdmlfileformat.h +++ b/common/pdmlfileformat.h @@ -19,24 +19,22 @@ along with this program. If not, see #ifndef _PDML_FILE_FORMAT_H #define _PDML_FILE_FORMAT_H -#include "protocol.pb.h" +#include "abstractfileformat.h" -#include - -class PdmlParser; - -class PdmlFileFormat : public QObject +class PdmlFileFormat : public AbstractFileFormat { - Q_OBJECT - public: PdmlFileFormat(); ~PdmlFileFormat(); - bool openStreams(const QString fileName, + virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); - bool saveStreams(const OstProto::StreamConfigList streams, + virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + }; extern PdmlFileFormat pdmlFileFormat; From f0e2b1ca503e502f19719dc2521762ab09dcab66 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 14 Mar 2011 20:47:46 +0530 Subject: [PATCH 126/294] Removed SAX based PDML parser --- common/pdml_p.cpp | 249 ---------------------------------------------- common/pdml_p.h | 31 ------ 2 files changed, 280 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index bb8e54d..1a8512e 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -103,255 +103,6 @@ void PdmlDefaultProtocol::unknownFieldHandler(QString name, } -#if 0 -// ---------------------------------------------------------- // -// PdmlParser -// ---------------------------------------------------------- // -PdmlParser::PdmlParser(OstProto::StreamConfigList *streams) -{ - skipCount_ = 0; - currentPdmlProtocol_ = NULL; - - streams_ = streams; - - protocolMap_.insert("unknown", new PdmlUnknownProtocol()); - protocolMap_.insert("geninfo", new PdmlGenInfoProtocol()); - protocolMap_.insert("frame", new PdmlFrameProtocol()); -#if 0 - protocolMap_.insert("fake-field-wrapper", - new PdmlFakeFieldWrapperProtocol()); -#endif -#if 0 - protocolMap_.insert("eth", new PdmlEthProtocol()); - protocolMap_.insert("ip", new PdmlIp4Protocol()); - protocolMap_.insert("ipv6", new PdmlIp6Protocol()); - protocolMap_.insert("tcp", new PdmlTcpProtocol()); -#endif -} - -PdmlParser::~PdmlParser() -{ - // TODO: free protocolMap_.values() -} - -bool PdmlParser::startElement(const QString & /* namespaceURI */, - const QString & /* localName */, - const QString &qName, - const QXmlAttributes &attributes) -{ - qDebug("%s (%s)", __FUNCTION__, qName.toAscii().constData()); - - if (skipCount_) - { - skipCount_++; - goto _exit; - } - - if (qName == "pdml") - { - packetCount_ = 0; - } - else if (qName == "packet") - { - // XXX: For now, each packet is converted to a stream - currentStream_ = streams_->add_stream(); - currentStream_->mutable_stream_id()->set_id(packetCount_); - currentStream_->mutable_core()->set_is_enabled(true); - - qDebug("packetCount_ = %d\n", packetCount_); - } - else if (qName == "proto") - { - QString protoName = attributes.value("name"); - if (protoName.isEmpty() - || (protoName == "expert")) - { - skipCount_++; - goto _exit; - } - - // HACK HACK HACK - if (currentPdmlProtocol_ && (currentPdmlProtocol_->ostProtoId() - == OstProto::Protocol::kHexDumpFieldNumber)) - currentPdmlProtocol_->postProtocolHandler(currentStream_); - - - if (!protocolMap_.contains(protoName)) - protoName = "unknown"; // FIXME: change to Ost:Hexdump - - currentPdmlProtocol_ = protocolMap_.value(protoName); - - Q_ASSERT(currentPdmlProtocol_ != NULL); - - int protoId = currentPdmlProtocol_->ostProtoId(); - - // PdmlDefaultProtocol => - if (protoId <= 0) - goto _exit; - - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id(protoId); - - const google::protobuf::Reflection *msgRefl = - proto->GetReflection(); - const google::protobuf::FieldDescriptor *fDesc = - msgRefl->FindKnownExtensionByNumber(protoId); - - // TODO: if !fDesc - // init default values of all fields in protocol - currentProtocolMsg_ = msgRefl->MutableMessage(proto, fDesc); - - currentPdmlProtocol_->preProtocolHandler(protoName, attributes, - currentStream_); - - } - else if (qName == "field") - { - // fields with "hide='yes'" are informational and should be skipped - if (attributes.value("hide") == "yes") - { - skipCount_++; - goto _exit; - } - - QString name = attributes.value("name"); - QString valueStr = attributes.value("value"); - int pos = -1; - int size = -1; - - if (!attributes.value("pos").isEmpty()) - pos = attributes.value("pos").toInt(); - if (!attributes.value("size").isEmpty()) - size = attributes.value("size").toInt(); - - qDebug("\tname:%s, pos:%d, size:%d value:%s", - name.toAscii().constData(), - pos, - size, - valueStr.toAscii().constData()); - - if (!currentPdmlProtocol_->hasField(name)) - { - currentPdmlProtocol_->unknownFieldHandler(name, pos, size, - attributes, currentStream_); - goto _exit; - } - - // TODO - int fId = currentPdmlProtocol_->fieldId(name); - const google::protobuf::Descriptor *msgDesc = - currentProtocolMsg_->GetDescriptor(); - const google::protobuf::FieldDescriptor *fDesc = - msgDesc->FindFieldByNumber(fId); - const google::protobuf::Reflection *msgRefl = - currentProtocolMsg_->GetReflection(); - - bool isOk; - - switch(fDesc->cpp_type()) - { - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO - case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: - msgRefl->SetUInt32(currentProtocolMsg_, fDesc, - valueStr.toUInt(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: - msgRefl->SetUInt64(currentProtocolMsg_, fDesc, - valueStr.toULongLong(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_STRING: - { - QByteArray hexVal = QByteArray::fromHex(valueStr.toUtf8()); - std::string str(hexVal.constData(), hexVal.size()); - msgRefl->SetString(currentProtocolMsg_, fDesc, str); - break; - } - default: - qDebug("%s: unhandled cpptype = %d", __FUNCTION__, - fDesc->cpp_type()); - } - } - -_exit: - return true; - -} - -bool PdmlParser::characters(const QString &str) -{ - //qDebug("%s (%s)", __FUNCTION__, str.toAscii().constData()); - //_currentText += str; - return true; -} - -bool PdmlParser::endElement(const QString & /* namespaceURI */, - const QString & /* localName */, - const QString &qName) -{ - qDebug("%s (%s)", __FUNCTION__, qName.toAscii().constData()); - - if (qName == "packet") - { - if (currentStream_->core().name().size()) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - hexDump->set_content(currentStream_->core().name()); - hexDump->set_pad_until_end(false); - currentStream_->mutable_core()->set_name(""); - } - packetCount_++; - currentPdmlProtocol_ = NULL; - goto _exit; - } - - if (skipCount_) - { - skipCount_--; - goto _exit; - } - - if (qName == "proto") - { - currentPdmlProtocol_->postProtocolHandler(currentStream_); - //currentPdmlProtocol_ = NULL; - } - else if (qName == "field") - { - } - -_exit: - return true; -} - -bool PdmlParser::fatalError(const QXmlParseException &exception) -{ - QString extra; - - qDebug("%s", __FUNCTION__); -#if 0 - if (exception.message() == "tag mismatch" && lastElement == "fieldData") - extra = "\nAre you using an old version of Wireshark? If so, try using a newer version. Alternatively, view the packet dump decode in Wireshark by clicking the \"External\" button."; -#endif - - QMessageBox::warning(0, QObject::tr("PDML Parser"), - QObject::tr("XML parse error for packet %1 " - "at line %2, column %3:\n %4\n%5") - .arg(packetCount_+1) - .arg(exception.lineNumber()) - .arg(exception.columnNumber()) - .arg(exception.message()) - .arg(extra)); - return false; -} -#endif - // ---------------------------------------------------------- // // PdmlReader // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index ab512eb..eca206f 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -60,37 +60,6 @@ protected: QMap fieldMap_; }; -#if 0 -class PdmlParser : public QXmlDefaultHandler -{ -public: - PdmlParser(OstProto::StreamConfigList *streams); - ~PdmlParser(); - - bool startElement(const QString &namespaceURI, - const QString &localName, - const QString &qName, - const QXmlAttributes &attributes); - bool endElement(const QString &namespaceURI, - const QString &localName, - const QString &qName); - bool characters(const QString &str); - bool fatalError(const QXmlParseException &exception); - -private: - void initProtocolMaps(); - - QMap protocolMap_; - PdmlDefaultProtocol *currentPdmlProtocol_; - int skipCount_; - int packetCount_; - OstProto::StreamConfigList *streams_; - - OstProto::Stream *currentStream_; - google::protobuf::Message *currentProtocolMsg_; -}; -#endif - class PdmlUnknownProtocol; class PcapFileFormat; class PdmlReader : public QXmlStreamReader From 9ceda3457a72d7c96fcde00fc80b4b4eb9b33f73 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 14 Mar 2011 20:58:52 +0530 Subject: [PATCH 127/294] Removed the 2 pass stuff --- common/pdml_p.cpp | 170 ++-------------------------------------------- common/pdml_p.h | 18 ----- 2 files changed, 4 insertions(+), 184 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 1a8512e..406c7a5 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -110,7 +110,6 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { gPdmlReader = this; pcap_ = NULL; - pass_ = 0; streams_ = streams; currentStream_ = NULL; @@ -139,30 +138,6 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) pcap_ = pcap; packetCount_ = 0; -#if 0 - // 1st pass - preprocessing fake fields - pass_ = 1; - qDebug("PASS %d\n", pass_); - while (!atEnd()) - { - readNext(); - if (isStartElement()) - { - if (name() == "pdml") - readPdml(); - else - raiseError("Not a pdml file!"); - } - } - - clear(); - device->seek(0); - setDevice(device); - -#endif - // 2nd pass - actual processing - pass_ = 2; - qDebug("PASS %d\n", pass_); while (!atEnd()) { readNext(); @@ -262,136 +237,13 @@ void PdmlReader::readPdml() if (isStartElement()) { if (name() == "packet") - { - if (pass_ == 1) - readPacketPass1(); - else if (pass_ == 2) - readPacket(); - else - Q_ASSERT(false); // unreachable! - } + readPacket(); else readUnexpectedElement(); } } } -/////////////////////// PASS 1 ////////////////////////// - -void PdmlReader::readPacketPass1() -{ - Q_ASSERT(isStartElement() && name() == "packet"); - - qDebug("Pass1 packetNum = %d", packetCount_); - - Fragment f; - f.pos = -1; - f.size = -1; - f.value = QByteArray(); - - pktFragments_.append(f); - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "proto") - readProtoPass1(); - else if (name() == "field") - skipElement(); - else - readUnexpectedElement(); - } - } - packetCount_++; -} - -void PdmlReader::readProtoPass1() -{ - Q_ASSERT(isStartElement() && name() == "proto"); - - if (attributes().value("name") != "fake-field-wrapper") - { - skipElement(); - return; - } - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "proto") - readProtoPass1(); - else if (name() == "field") - readFieldPass1(); - else - readUnexpectedElement(); - } - } -} - -void PdmlReader::readFieldPass1() -{ - Q_ASSERT(isStartElement() && name() == "field"); - - if (attributes().value("name") != "data.data") - { - skipElement(); - return; - } - - QString fieldName = attributes().value("name").toString(); - QString valueHexStr = attributes().value("value").toString(); - int pos = -1; - int size = -1; - - if (!attributes().value("pos").isEmpty()) - pos = attributes().value("pos").toString().toInt(); - if (!attributes().value("size").isEmpty()) - size = attributes().value("size").toString().toInt(); - - qDebug("\tFAKE FIELD fieldName:%s, pos:%d, size:%d value:%s", - fieldName.toAscii().constData(), - pos, - size, - valueHexStr.toAscii().constData()); - - pktFragments_[packetCount_-1].pos = pos; - pktFragments_[packetCount_-1].size = size; - pktFragments_[packetCount_-1].value = - QByteArray::fromHex(valueHexStr.toUtf8()); - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "proto") - readProtoPass1(); - else if (name() == "field") - readFieldPass1(); - else - readUnexpectedElement(); - } - } -} - -/////////////////////// PASS 2 ////////////////////////// - void PdmlReader::readPacket() { PcapFileFormat::PcapPacketHeader pktHdr; @@ -890,24 +742,10 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, // Skipped field? Pad with zero! if ((pos > expPos_) && (expPos_ < endPos_)) { -#if 0 - PdmlReader::Fragment f; + QByteArray hexVal(pos - expPos_, char(0)); - f = gPdmlReader->pktFragments_.value(stream->stream_id().id()-1); - - if (expPos_ == f.pos) - { - hexDump->mutable_content()->append(f.value.constData(), f.size); - expPos_ += f.size; - } - else -#endif - { - QByteArray hexVal(pos - expPos_, char(0)); - - hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); - expPos_ += hexVal.size(); - } + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); } if ((pos == expPos_) /*&& (pos < endPos_)*/) diff --git a/common/pdml_p.h b/common/pdml_p.h index eca206f..ca54028 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -91,20 +91,6 @@ private: typedef PdmlDefaultProtocol* (*FactoryMethod)(); -#if 0 - class PacketFragment // TODO: find a better name! - { - public: - private: - typedef struct - { - int pos; - int size; - QByteArray value; - } Fragment; - QList - }; -#endif typedef struct { int pos; @@ -117,7 +103,6 @@ private: OstProto::StreamConfigList *streams_; PcapFileFormat *pcap_; - int pass_; int packetCount_; int expPos_; bool skipUntilEnd_; @@ -126,9 +111,6 @@ private: QList pktFragments_; QByteArray pktBuf_; - - //PdmlDefaultProtocol *currentPdmlProtocol_; - //google::protobuf::Message *currentProtocolMsg_; }; class PdmlUnknownProtocol : public PdmlDefaultProtocol From f99774a85128a0adc6a215931386daee27188f07 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 14 Mar 2011 21:08:05 +0530 Subject: [PATCH 128/294] Removed FakeFieldWrapper Protocol --- common/pdml_p.cpp | 66 ----------------------------------------------- common/pdml_p.h | 26 ------------------- 2 files changed, 92 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 406c7a5..ae1c7ce 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -118,10 +118,6 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); -#if 0 - factory_.insert("fake-field-wrapper", - new PdmlFakeFieldWrapperProtocol()); -#endif factory_.insert("eth",PdmlEthProtocol::createInstance); factory_.insert("ip",PdmlIp4Protocol::createInstance); factory_.insert("ipv6",PdmlIp6Protocol::createInstance); @@ -834,68 +830,6 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, } } -#if 1 -// ---------------------------------------------------------- // -// PdmlFakeFieldWrapperProtocol // -// ---------------------------------------------------------- // - -PdmlFakeFieldWrapperProtocol::PdmlFakeFieldWrapperProtocol() -{ - pdmlProtoName_ = "OST:HexDump"; - ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; - - expPos_ = -1; -} - -PdmlDefaultProtocol* PdmlFakeFieldWrapperProtocol::createInstance() -{ - return new PdmlFakeFieldWrapperProtocol(); -} - -void PdmlFakeFieldWrapperProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Stream *stream) -{ - expPos_ = 0; - OstProto::HexDump *hexDump = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - hexDump->set_pad_until_end(false); -} - -void PdmlFakeFieldWrapperProtocol::postProtocolHandler(OstProto::Stream *stream) -{ - OstProto::HexDump *hexDump = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - - qDebug("%s: expPos_ = %d\n", __FUNCTION__, expPos_); - - // TODO: if content size is zero, remove protocol? - - hexDump->set_pad_until_end(false); - expPos_ = -1; -} - -void PdmlFakeFieldWrapperProtocol::unknownFieldHandler(QString name, int pos, - int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) -{ - OstProto::HexDump *hexDump = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); - - if ((pos == expPos_) && (size >= 0) && - (!name.startsWith("tcp.segment")) && - (!attributes.value("unmaskedvalue").isEmpty() || - !attributes.value("value").isEmpty())) - { - QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? - QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : - QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); - - hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); - expPos_ += hexVal.size(); - } -} - -#endif // ---------------------------------------------------------- // // PdmlEthProtocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index ca54028..70423ea 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -91,13 +91,6 @@ private: typedef PdmlDefaultProtocol* (*FactoryMethod)(); - typedef struct - { - int pos; - int size; - QByteArray value; - } Fragment; - QMap factory_; OstProto::StreamConfigList *streams_; @@ -152,25 +145,6 @@ public: const QXmlStreamAttributes &attributes, OstProto::Stream *stream); }; -#if 1 -class PdmlFakeFieldWrapperProtocol : public PdmlDefaultProtocol -{ -public: - PdmlFakeFieldWrapperProtocol(); - - static PdmlDefaultProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); -private: - int expPos_; -}; -#endif - class PdmlEthProtocol : public PdmlDefaultProtocol { public: From 1e805661fc679e5c12bf70cc0d0034e527d54681 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 14 Mar 2011 21:08:59 +0530 Subject: [PATCH 129/294] Fixed compilation error --- common/pdml_p.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common/pdml_p.h b/common/pdml_p.h index 70423ea..42ea327 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -101,7 +101,6 @@ private: bool skipUntilEnd_; OstProto::Stream *prevStream_; OstProto::Stream *currentStream_; - QList pktFragments_; QByteArray pktBuf_; }; From 00e03a44fdcbda96f0f3e6dcb6a43b8ca224894e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 17 Mar 2011 22:00:51 +0530 Subject: [PATCH 130/294] Open/Save Progress Dialog done --- client/port.cpp | 103 +++++++++++++++++++++++++++++++--- client/portswindow.cpp | 5 +- common/abstractfileformat.cpp | 46 ++++++++++++++- common/abstractfileformat.h | 40 ++++++++++++- common/fileformat.cpp | 3 + common/pcapfileformat.cpp | 36 +++++++++++- common/pdml_p.cpp | 10 +++- common/pdml_p.h | 9 ++- common/pdmlfileformat.cpp | 38 ++----------- 9 files changed, 237 insertions(+), 53 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 90992ab..3130432 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -22,10 +22,14 @@ along with this program. If not, see #include "abstractfileformat.h" #include +#include +#include #include #include #include +extern QMainWindow *mainWindow; + uint Port::mAllocStreamId = 0; uint Port::newStreamId() @@ -225,52 +229,137 @@ void Port::updateStats(OstProto::PortStats *portStats) bool Port::openStreams(QString fileName, bool append, QString &error) { + bool ret = false; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); OstProto::StreamConfigList streams; AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); if (fmt == NULL) goto _fail; - if (!fmt->openStreams(fileName, streams, error)) + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); if (!append) { - while (numStreams()) + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } } + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); for (int i = 0; i < streams.stream_size(); i++) { + if (progress.wasCanceled()) + goto _user_cancel; newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); } +_user_cancel: emit streamListChanged(mPortGroupId, mPortId); - - return true; + ret = true; _fail: - return false; + progress.close(); + mainWindow->setEnabled(true); + return ret; } bool Port::saveStreams(QString fileName, QString fileType, QString &error) { + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); OstProto::StreamConfigList streams; if (fmt == NULL) goto _fail; + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); streams.mutable_port_id()->set_id(0); for (int i = 0; i < mStreams.size(); i++) { OstProto::Stream *s = streams.add_stream(); mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); } - return fmt->saveStreams(streams, fileName, error); + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; _fail: error = QString("Unsupported File Type - %1").arg(fileType); - return false; + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; } diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 86bcf71..a2cf238 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -458,14 +458,15 @@ void PortsWindow::on_actionOpen_Streams_triggered() Q_ASSERT(plm->isPort(current)); - fileName = QFileDialog::getOpenFileName(this, tr("Open Streams")); + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), "D:/srivatsp/projects/ostinato/testfiles/pcaps"); if (fileName.isEmpty()) goto _exit; if (tvStreamList->model()->rowCount()) { QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), - tr("Append to existing streams? Or overwrite?")); + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); QPushButton *appendBtn = msgBox.addButton(tr("Append"), QMessageBox::ActionRole); QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp index cb3fa43..f748447 100644 --- a/common/abstractfileformat.cpp +++ b/common/abstractfileformat.cpp @@ -27,6 +27,7 @@ along with this program. If not, see AbstractFileFormat::AbstractFileFormat() { + stop_ = false; } AbstractFileFormat::~AbstractFileFormat() @@ -40,10 +41,40 @@ QStringList AbstractFileFormat::supportedFileTypes() << "PCAP (*)" << "PDML (*.pdml)"; } + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( const QString fileName) { - if (fileFormat.isMyFileFormat(fileName)) return &fileFormat; @@ -71,3 +102,16 @@ AbstractFileFormat* AbstractFileFormat::fileFormatFromType( return NULL; } + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h index 7a648ee..031e4e1 100644 --- a/common/abstractfileformat.h +++ b/common/abstractfileformat.h @@ -22,12 +22,12 @@ along with this program. If not, see #include "protocol.pb.h" -#include +#include #include -class AbstractFileFormat : public QObject +class AbstractFileFormat : public QThread { - Q_OBJECT + Q_OBJECT public: AbstractFileFormat(); virtual ~AbstractFileFormat(); @@ -37,6 +37,13 @@ public: virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) = 0; + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + static AbstractFileFormat* fileFormatFromFile(const QString fileName); static AbstractFileFormat* fileFormatFromType(const QString fileType); @@ -46,6 +53,33 @@ public: bool isMyFileFormat(const QString fileName) = 0; bool isMyFileType(const QString fileType) = 0; #endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + }; #endif diff --git a/common/fileformat.cpp b/common/fileformat.cpp index 7ddc65b..aada8b1 100644 --- a/common/fileformat.cpp +++ b/common/fileformat.cpp @@ -322,6 +322,8 @@ bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, goto _zero_cksum_serialize_fail; } + emit status("Calculating checksum..."); + // Calculate and write checksum calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); cksum.set_value(calcCksum); @@ -335,6 +337,7 @@ bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, qDebug("Writing %d bytes", buf.size()); //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + emit status("Writing to disk..."); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) goto _open_fail; diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index a893565..04ace99 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -64,7 +64,7 @@ PcapFileFormat::~PcapFileFormat() bool PcapFileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { - bool viaPdml = true; // TODO: shd be a param to function + bool viaPdml = false; // TODO: shd be a param to function bool isOk = false; QFile file(fileName); @@ -77,6 +77,8 @@ bool PcapFileFormat::openStreams(const QString fileName, OstProto::Stream *prevStream = NULL; uint lastUsec = 0; int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; QByteArray pktBuf; if (!file.open(QIODevice::ReadOnly)) @@ -90,6 +92,9 @@ bool PcapFileFormat::openStreams(const QString fileName, { QProcess gzip; + emit status("Decompressing..."); + emit target(0); + if (!file2.open()) { error.append("Unable to open temporary file to uncompress .gz\n"); @@ -126,6 +131,11 @@ bool PcapFileFormat::openStreams(const QString fileName, fd_.setDevice(&file); } + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + fd_ >> magic; qDebug("magic = %08x", magic); @@ -173,6 +183,8 @@ bool PcapFileFormat::openStreams(const QString fileName, } qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); tshark.setStandardOutputFile(pdmlFile.fileName()); // FIXME: hardcoded prog name @@ -193,12 +205,18 @@ bool PcapFileFormat::openStreams(const QString fileName, goto _non_pdml; } - isOk = reader.read(&pdmlFile, this); // TODO: pass error string? + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); // TODO: pass error string? goto _exit; } _non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage pktCount = 1; while (!fd_.atEnd()) { @@ -235,11 +253,20 @@ _non_pdml: lastUsec = usec; prevStream = stream; pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; } isOk = true; goto _exit; +_user_cancel: + isOk = true; + goto _exit; + #if 1 _err_unsupported_encap: error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " @@ -344,6 +371,9 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, pktBuf.resize(kMaxSnapLen); + emit status("Writing Packets..."); + emit target(streams.stream_size()); + pktHdr.tsSec = 0; pktHdr.tsUsec = 0; for (int i = 0; i < streams.stream_size(); i++) @@ -377,6 +407,8 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, pktHdr.tsSec++; pktHdr.tsUsec -= 1000000; } + + emit progress(i); } file.close(); diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index ae1c7ce..3dac8e0 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -115,6 +115,8 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) currentStream_ = NULL; prevStream_ = NULL; + stop_ = NULL; + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); @@ -128,12 +130,13 @@ PdmlReader::~PdmlReader() { } -bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) { setDevice(device); pcap_ = pcap; packetCount_ = 0; + stop_ = stop; while (!atEnd()) { readNext(); @@ -146,7 +149,7 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap) } } - if (error()) + if (error() && (errorString() != "USER-CANCEL")) { qDebug("Line %lld", lineNumber()); qDebug("Col %lld", columnNumber()); @@ -325,8 +328,11 @@ void PdmlReader::readPacket() } packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); if (prevStream_) prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); } void PdmlReader::readProto() diff --git a/common/pdml_p.h b/common/pdml_p.h index 42ea327..af7c4f6 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -62,14 +62,18 @@ protected: class PdmlUnknownProtocol; class PcapFileFormat; -class PdmlReader : public QXmlStreamReader +class PdmlReader : public QObject, public QXmlStreamReader { + Q_OBJECT friend class PdmlUnknownProtocol; public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); - bool read(QIODevice *device, PcapFileFormat *pcap = NULL); + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); private: PdmlDefaultProtocol* allocPdmlProtocol(QString protoName); @@ -103,6 +107,7 @@ private: OstProto::Stream *currentStream_; QByteArray pktBuf_; + bool *stop_; }; class PdmlUnknownProtocol : public PdmlDefaultProtocol diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 0c51652..1e2c4c8 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -30,39 +30,6 @@ PdmlFileFormat::~PdmlFileFormat() { } -#if 0 -bool PdmlFileFormat::openStreams(const QString fileName, - OstProto::StreamConfigList &streams, QString &error) -{ - bool isOk; - QFile file(fileName); - PdmlParser *pdml; - QXmlSimpleReader *xmlReader; - QXmlInputSource *xmlSource; - - if (!file.open(QIODevice::ReadOnly)) - goto _open_fail; - - pdml = new PdmlParser(&streams); - xmlSource = new QXmlInputSource(&file); - xmlReader = new QXmlSimpleReader; - xmlReader->setContentHandler(pdml); - xmlReader->setErrorHandler(pdml); - isOk = xmlReader->parse(xmlSource, false); // non-incremental parse - - goto _exit; - -_open_fail: - isOk = false; - -_exit: - delete xmlReader; - delete xmlSource; - delete pdml; - - return isOk; -} -#else bool PdmlFileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { @@ -73,6 +40,10 @@ bool PdmlFileFormat::openStreams(const QString fileName, if (!file.open(QIODevice::ReadOnly)) goto _open_fail; + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + // TODO: fill in error string isOk = reader->read(&file); @@ -87,7 +58,6 @@ _exit: return isOk; } -#endif bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) From bb78afbca7141461f47136949d4a96360c7f2d2d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 19 Mar 2011 11:48:58 +0530 Subject: [PATCH 131/294] Implemented "Import Options" for PCAP file format --- client/port.cpp | 12 ++++++++++++ common/abstractfileformat.cpp | 10 ++++++++++ common/abstractfileformat.h | 9 +++++++-- common/ostproto.pro | 1 + common/pcapfileformat.cpp | 34 +++++++++++++++++++++++++++++++--- common/pcapfileformat.h | 19 +++++++++++++++++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 3130432..8068c41 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -230,6 +230,7 @@ void Port::updateStats(OstProto::PortStats *portStats) bool Port::openStreams(QString fileName, bool append, QString &error) { bool ret = false; + QDialog *optDialog; QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); OstProto::StreamConfigList streams; AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); @@ -237,6 +238,16 @@ bool Port::openStreams(QString fileName, bool append, QString &error) if (fmt == NULL) goto _fail; + if (optDialog = fmt->openOptionsDialog()) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + progress.setAutoReset(false); progress.setAutoClose(false); progress.setMinimumDuration(0); @@ -295,6 +306,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error) _user_cancel: emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: ret = true; _fail: diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp index f748447..15271d7 100644 --- a/common/abstractfileformat.cpp +++ b/common/abstractfileformat.cpp @@ -34,6 +34,16 @@ AbstractFileFormat::~AbstractFileFormat() { } +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + QStringList AbstractFileFormat::supportedFileTypes() { return QStringList() diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h index 031e4e1..1f8447d 100644 --- a/common/abstractfileformat.h +++ b/common/abstractfileformat.h @@ -25,6 +25,8 @@ along with this program. If not, see #include #include +class QDialog; + class AbstractFileFormat : public QThread { Q_OBJECT @@ -37,6 +39,9 @@ public: virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) = 0; + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + void openStreamsOffline(const QString fileName, OstProto::StreamConfigList &streams, QString &error); void saveStreamsOffline(const OstProto::StreamConfigList streams, @@ -44,11 +49,11 @@ public: bool result(); + static QStringList supportedFileTypes(); + static AbstractFileFormat* fileFormatFromFile(const QString fileName); static AbstractFileFormat* fileFormatFromType(const QString fileType); - static QStringList supportedFileTypes(); - #if 0 bool isMyFileFormat(const QString fileName) = 0; bool isMyFileType(const QString fileType) = 0; diff --git a/common/ostproto.pro b/common/ostproto.pro index 3283090..7e3c80a 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -5,6 +5,7 @@ INCLUDEPATH += "../extra/qhexedit2/src" LIBS += \ -lprotobuf FORMS += \ + pcapfileimport.ui \ mac.ui \ payload.ui \ eth2.ui \ diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 04ace99..4d8a46a 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -53,19 +53,39 @@ const quint32 kDltEthernet = 1; PcapFileFormat pcapFileFormat; +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + PcapFileFormat::PcapFileFormat() { + importDialog_ = NULL; } PcapFileFormat::~PcapFileFormat() { + delete importDialog_; } bool PcapFileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { - bool viaPdml = false; // TODO: shd be a param to function - bool isOk = false; QFile file(fileName); QTemporaryFile file2; @@ -170,7 +190,7 @@ bool PcapFileFormat::openStreams(const QString fileName, pktBuf.resize(fileHdr.snapLen); - if (viaPdml) + if (importOptions_.value("ViaPdml").toBool()) { QTemporaryFile pdmlFile; PdmlReader reader(&streams); @@ -424,6 +444,14 @@ _exit: return isOk; } +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + bool PcapFileFormat::isMyFileFormat(const QString fileName) { // TODO diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h index 4f7ab7a..064aaf1 100644 --- a/common/pcapfileformat.h +++ b/common/pcapfileformat.h @@ -20,8 +20,23 @@ along with this program. If not, see #define _PCAP_FILE_FORMAT_H #include "abstractfileformat.h" +#include "ui_pcapfileimport.h" #include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; class PdmlReader; class PcapFileFormat : public AbstractFileFormat @@ -37,6 +52,8 @@ public: bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); + virtual QDialog* openOptionsDialog(); + bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); @@ -61,6 +78,8 @@ private: bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; }; extern PcapFileFormat pcapFileFormat; From efd19fc96bb761b723fd57c0c382443e5bb17a0f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 19 Mar 2011 20:29:07 +0530 Subject: [PATCH 132/294] Implemented the PCAP Diff --- client/portswindow.cpp | 21 +++- common/pcapfileformat.cpp | 214 +++++++++++++++++++++++++++++++++++++- 2 files changed, 226 insertions(+), 9 deletions(-) diff --git a/client/portswindow.cpp b/client/portswindow.cpp index a2cf238..1a7fdb5 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -455,6 +455,7 @@ void PortsWindow::on_actionOpen_Streams_triggered() QString fileName; QString errorStr; bool append = true; + bool ret; Q_ASSERT(plm->isPort(current)); @@ -485,10 +486,22 @@ void PortsWindow::on_actionOpen_Streams_triggered() Q_ASSERT(false); } - if (!plm->port(current).openStreams(fileName, append, errorStr)) - QMessageBox::critical(this, qApp->applicationName(), errorStr); - else if (!errorStr.isEmpty()) - QMessageBox::warning(this, qApp->applicationName(), errorStr); + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + _exit: return; } diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 4d8a46a..89e122b 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -58,6 +58,10 @@ PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) { setupUi(this); options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); } @@ -75,6 +79,9 @@ void PcapImportOptionsDialog::accept() PcapFileFormat::PcapFileFormat() { + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + importDialog_ = NULL; } @@ -192,9 +199,9 @@ bool PcapFileFormat::openStreams(const QString fileName, if (importOptions_.value("ViaPdml").toBool()) { + QProcess tshark; QTemporaryFile pdmlFile; PdmlReader reader(&streams); - QProcess tshark; if (!pdmlFile.open()) { @@ -229,9 +236,205 @@ bool PcapFileFormat::openStreams(const QString fileName, emit status("Reading PDML packets..."); emit target(100); // in percentage - isOk = reader.read(&pdmlFile, this, &stop_); // TODO: pass error string? + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + // FIXME: hardcoded prog name + diff.start("D:/srivatsp/projects/ostinato/pdml/bin/diff.exe", + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + goto _exit; - } _non_pdml: @@ -287,13 +490,14 @@ _user_cancel: isOk = true; goto _exit; -#if 1 +_diff_fail: + goto _exit; + _err_unsupported_encap: error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " "not supported - Sorry!")) .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); goto _exit; -#endif _err_unsupported_version: error = QString(tr("%1 is in PCAP version %2.%3 format which is " From cb70fd83e650c8bb637024a625d53e46f9115f5b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 20 Mar 2011 17:21:14 +0530 Subject: [PATCH 133/294] Missed out the PCAP Import UI in the last commit --- common/pcapfileimport.ui | 132 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 common/pcapfileimport.ui diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + From 71382ae4ca6c569fb6706fe1381d650feed3f8db Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 20 Mar 2011 20:50:07 +0530 Subject: [PATCH 134/294] Implemented configuration of external application paths in preferences. Removed all hardcoded paths for external applications. --- client/main.cpp | 20 +++++ client/preferences.cpp | 63 +++++++++++++++ client/preferences.h | 4 + client/preferences.ui | 162 ++++++++++++++++++++++++++++++++------ client/settings.h | 38 +++++++++ common/ostproto.pro | 2 + common/ostprotolib.cpp | 55 +++++++++++++ common/ostprotolib.h | 42 ++++++++++ common/pcapfileformat.cpp | 40 ++++------ 9 files changed, 376 insertions(+), 50 deletions(-) create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h diff --git a/client/main.cpp b/client/main.cpp index a13ff77..07b073f 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -18,7 +18,9 @@ along with this program. If not, see */ #include "mainwindow.h" +#include "../common/ostprotolib.h" #include "../common/protocolmanager.h" +#include "settings.h" #include #include @@ -31,11 +33,23 @@ extern ProtocolManager *OstProtocolManager; QSettings *appSettings; QMainWindow *mainWindow; +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + int main(int argc, char* argv[]) { QApplication app(argc, argv); int exitCode; +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + app.setApplicationName("Ostinato"); app.setOrganizationName("Ostinato"); app.setProperty("version", version); @@ -53,6 +67,12 @@ int main(int argc, char* argv[]) else appSettings = new QSettings(); + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + mainWindow = new MainWindow; mainWindow->show(); exitCode = app.exec(); diff --git a/client/preferences.cpp b/client/preferences.cpp index 4f372ee..0e54bbd 100644 --- a/client/preferences.cpp +++ b/client/preferences.cpp @@ -19,6 +19,7 @@ along with this program. If not, see #include "preferences.h" +#include "../common/ostprotolib.h" #include "settings.h" #include @@ -31,6 +32,14 @@ Preferences::Preferences() wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); } Preferences::~Preferences() @@ -40,6 +49,16 @@ Preferences::~Preferences() void Preferences::accept() { appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); QDialog::accept(); } @@ -54,3 +73,47 @@ void Preferences::on_wiresharkPathButton_clicked() if (!path.isEmpty()) wiresharkPathEdit->setText(path); } + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h index 1821346..78109ab 100644 --- a/client/preferences.h +++ b/client/preferences.h @@ -36,6 +36,10 @@ public slots: private slots: void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); }; #endif diff --git a/client/preferences.ui b/client/preferences.ui index 514ed42..d64b4cb 100644 --- a/client/preferences.ui +++ b/client/preferences.ui @@ -6,7 +6,7 @@ 0 0 400 - 164 + 220 @@ -24,37 +24,136 @@ QFrame::Sunken - - - - - - - Wireshark Path - - - - - - - - - - ... - - - - + + + + + 'wireshark' Path + + + wiresharkPathEdit + + - + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + Qt::Vertical - 20 - 40 + 21 + 61 @@ -74,6 +173,19 @@ + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + diff --git a/client/settings.h b/client/settings.h index 3fbf4cf..2f0666c 100644 --- a/client/settings.h +++ b/client/settings.h @@ -36,6 +36,44 @@ const QString kWiresharkPathDefaultValue( const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); #endif +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + #endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 7e3c80a..3fe62f2 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -56,6 +56,7 @@ PROTOS += \ hexdump.proto \ sample.proto HEADERS += \ + ostprotolib.h \ abstractprotocol.h \ comboprotocol.h \ abstractfileformat.h \ @@ -98,6 +99,7 @@ HEADERS += \ hexdump.h \ sample.h SOURCES += \ + ostprotolib.cpp \ abstractprotocol.cpp \ crc32c.cpp \ abstractfileformat.cpp \ diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..e46dc8c --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} + diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 89e122b..106399f 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -20,6 +20,7 @@ along with this program. If not, see #include "pcapfileformat.h" #include "pdml_p.h" +#include "ostprotolib.h" #include "streambase.h" #include "hexdump.pb.h" @@ -131,15 +132,14 @@ bool PcapFileFormat::openStreams(const QString fileName, qDebug("decompressing to %s", file2.fileName().toAscii().constData()); gzip.setStandardOutputFile(file2.fileName()); - // FIXME: hardcoded prog name - gzip.start("C:/Program Files/CmdLineTools/gzip.exe", + gzip.start(OstProtoLib::gzipPath(), QStringList() << "-d" << "-c" << fileName); if (!gzip.waitForStarted(-1)) { - error.append(QString("Unable to start gzip\n")); + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); goto _err_unzip_fail; } @@ -214,15 +214,14 @@ bool PcapFileFormat::openStreams(const QString fileName, emit target(0); tshark.setStandardOutputFile(pdmlFile.fileName()); - // FIXME: hardcoded prog name - tshark.start("C:/Program Files/Wireshark/Tshark.exe", + tshark.start(OstProtoLib::tsharkPath(), QStringList() << QString("-r%1").arg(fileName) << "-otcp.desegment_tcp_streams:FALSE" << "-Tpdml"); if (!tshark.waitForStarted(-1)) { - error.append(QString("Unable to start tshark\n")); + error.append(QString("Unable to start tshark. Check path in preferences.\n")); goto _non_pdml; } @@ -287,27 +286,23 @@ bool PcapFileFormat::openStreams(const QString fileName, tshark.setStandardOutputProcess(&awk); awk.setStandardOutputFile(originalTextFile.fileName()); - // FIXME: hardcoded prog name - tshark.start("C:/Program Files/Wireshark/Tshark.exe", + tshark.start(OstProtoLib::tsharkPath(), QStringList() << QString("-r%1").arg(fileName) << "-otcp.desegment_tcp_streams:FALSE" << "-x"); if (!tshark.waitForStarted(-1)) { - error.append(QString("Unable to start tshark - %1\n") - .arg(tshark.exitCode())); + error.append(QString("Unable to start tshark. Check path in Preferences.\n")); goto _diff_fail; } - // FIXME: hardcoded prog name - awk.start("D:/srivatsp/projects/ostinato/pdml/bin/gawk.exe", + awk.start(OstProtoLib::awkPath(), QStringList() << kAwkFilter); if (!awk.waitForStarted(-1)) { tshark.kill(); - error.append(QString("Unable to start awk - %1\n") - .arg(awk.exitCode())); + error.append(QString("Unable to start awk. Check path in Preferences.\n")); goto _diff_fail; } @@ -351,27 +346,23 @@ bool PcapFileFormat::openStreams(const QString fileName, tshark.setStandardOutputProcess(&awk); awk.setStandardOutputFile(importedTextFile.fileName()); - // FIXME: hardcoded prog name - tshark.start("C:/Program Files/Wireshark/Tshark.exe", + tshark.start(OstProtoLib::tsharkPath(), QStringList() << QString("-r%1").arg(importedPcapFile.fileName()) << "-otcp.desegment_tcp_streams:FALSE" << "-x"); if (!tshark.waitForStarted(-1)) { - error.append(QString("Unable to start tshark - %1\n") - .arg(tshark.exitCode())); + error.append(QString("Unable to start tshark. Check path in Preferences.\n")); goto _diff_fail; } - // FIXME: hardcoded prog name - awk.start("D:/srivatsp/projects/ostinato/pdml/bin/gawk.exe", + awk.start(OstProtoLib::awkPath(), QStringList() << kAwkFilter); if (!awk.waitForStarted(-1)) { tshark.kill(); - error.append(QString("Unable to start awk - %1\n") - .arg(awk.exitCode())); + error.append(QString("Unable to start awk. Check path in Preferences.\n")); goto _diff_fail; } @@ -401,8 +392,7 @@ bool PcapFileFormat::openStreams(const QString fileName, emit target(0); diff.setStandardOutputFile(diffFile.fileName()); - // FIXME: hardcoded prog name - diff.start("D:/srivatsp/projects/ostinato/pdml/bin/diff.exe", + diff.start(OstProtoLib::diffPath(), QStringList() << "-u" << "-F^ [1-9]" @@ -414,7 +404,7 @@ bool PcapFileFormat::openStreams(const QString fileName, << importedTextFile.fileName()); if (!diff.waitForStarted(-1)) { - error.append(QString("Unable to start diff\n") + error.append(QString("Unable to start diff. Check path in Preferences.\n") .arg(diff.exitCode())); goto _diff_fail; } From b3f2e0483fda99c480cb639c0cf77a45420a05fb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 20 Mar 2011 20:59:39 +0530 Subject: [PATCH 135/294] Fixed invalid FAQ URL --- client/portgroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 63bc968..bbea61d 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -168,7 +168,7 @@ void PortGroup::when_portListChanged(quint32 /*portGroupId*/) "Please ensure that you are running 'drone' - the server " "component of Ostinato with admin/root OR setuid privilege.\n\n" "For more information see " - "http://ostinato.googlecode.com/wiki/FAQ#" + "http://code.google.com/p/ostinato/wiki/FAQ#" "Q._Port_group_has_no_interfaces") .arg(serverAddress().toString()) .arg(int(serverPort()))); From 497a0be27645ecae80f1afea19e60471cad2ee7a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 21 Mar 2011 22:25:34 +0530 Subject: [PATCH 136/294] Added code to remember the last directory for open/save streams. Also to remember the StreamConfigDialog geometry and protocol data tab --- client/portswindow.cpp | 9 +++++++-- client/streamconfigdialog.cpp | 18 ++++++++++++------ client/streamconfigdialog.h | 6 ++++-- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 1a7fdb5..837fb89 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -23,6 +23,7 @@ along with this program. If not, see #include "streamconfigdialog.h" #include "streamlistdelegate.h" +#include #include #include #include @@ -452,6 +453,7 @@ void PortsWindow::on_actionOpen_Streams_triggered() qDebug("Open Streams Action"); QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; QString fileName; QString errorStr; bool append = true; @@ -459,7 +461,7 @@ void PortsWindow::on_actionOpen_Streams_triggered() Q_ASSERT(plm->isPort(current)); - fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), "D:/srivatsp/projects/ostinato/testfiles/pcaps"); + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); if (fileName.isEmpty()) goto _exit; @@ -501,6 +503,7 @@ void PortsWindow::on_actionOpen_Streams_triggered() msgBox.exec(); } + dirName = QFileInfo(fileName).absolutePath(); _exit: return; @@ -511,7 +514,7 @@ void PortsWindow::on_actionSave_Streams_triggered() qDebug("Save Streams Action"); QModelIndex current = tvPortList->selectionModel()->currentIndex(); - QString fileName; + static QString fileName; QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); QString fileType; QString errorStr; @@ -553,6 +556,8 @@ _retry: QMessageBox::critical(this, qApp->applicationName(), errorStr); else if (!errorStr.isEmpty()) QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); _exit: return; } diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 981fabc..ad81cbf 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -30,7 +30,9 @@ along with this program. If not, see #include "../common/protocolmanager.h" extern ProtocolManager *OstProtocolManager; +QRect StreamConfigDialog::lastGeometry; int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) @@ -164,7 +166,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); - // Finally, restore the saved last selected tab for the various tab widgets + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); twTopLevel->setCurrentIndex(lastTopLevelTabIndex); } @@ -532,13 +537,9 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) // Protocol Data case 1: { - QWidget *selWidget; - // Hide the ToolBox before modifying it - else we have a crash !!! tbProtocolData->hide(); - selWidget = tbProtocolData->currentWidget(); - // Remove all existing protocol widgets while (tbProtocolData->count() > 0) { @@ -555,7 +556,8 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) tbProtocolData->addItem(p->configWidget(), p->name()); } - tbProtocolData->setCurrentWidget(selWidget); + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); tbProtocolData->show(); break; @@ -572,6 +574,8 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) default: break; } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); } void StreamConfigDialog::update_NumPacketsAndNumBursts() @@ -998,7 +1002,9 @@ void StreamConfigDialog::on_pbOk_clicked() qDebug("stream stored"); + lastGeometry = geometry(); lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); accept(); } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index bb91ce7..8321eba 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -84,8 +84,10 @@ private: // The following static variables are used to track the "selected" tab // for the various tab widgets so that it can be restored when the dialog - // is opened the next time - static int lastTopLevelTabIndex; + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; void setupUiExtra(); void LoadCurrentStream(); From 3a713899a97009143cf9c62e738814773f845c8e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 22 Mar 2011 23:01:05 +0530 Subject: [PATCH 137/294] Updated the PCAP import/export test code to work with latest code --- test/main.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/test/main.cpp b/test/main.cpp index c69ccbd..94ff561 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,6 +1,6 @@ +#include "ostprotolib.h" #include "pcapfileformat.h" -#include "pdmlfileformat.h" #include "protocol.pb.h" #include "protocolmanager.h" @@ -11,6 +11,7 @@ extern ProtocolManager *OstProtocolManager; int main(int argc, char* argv[]) { + bool isOk; QCoreApplication app(argc, argv); QString error; @@ -26,19 +27,33 @@ int main(int argc, char* argv[]) QString inFile(argv[1]); QString outFile(argv[2]); - if (!pcapFileFormat.openStreams(inFile, streams, error)) + OstProtocolManager = new ProtocolManager(); + + OstProtoLib::setExternalApplicationPaths( + "c:/Program Files/Wireshark/Tshark.exe", + "d:/srivatsp/projects/ostinato/pdml/bin/gzip.exe", + "d:/srivatsp/projects/ostinato/pdml/bin/diff.exe", + "d:/srivatsp/projects/ostinato/pdml/bin/gawk.exe"); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) { fprintf(stdout, "failed reading streams from %s:%s\n", inFile.toAscii().constData(), error.toAscii().constData()); - exit(1); } - if (!pcapFileFormat.saveStreams(streams, outFile, error)) + if (!isOk) + exit(1); + + isOk = pcapFileFormat.saveStreams(streams, outFile, error); + if (!error.isEmpty()) { fprintf(stdout, "failed writing streams to %s:%s\n", outFile.toAscii().constData(), error.toAscii().constData()); - exit(1); } + if (!isOk) + exit(1); + return 0; } From 3fab6b207a4e25141bacb0ce91174d0549d7842e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 24 Mar 2011 21:58:39 +0530 Subject: [PATCH 138/294] First round of cleanup of the PDML import code --- common/pdml_p.cpp | 364 ++++++++++++++++++++-------------------------- common/pdml_p.h | 28 ++-- 2 files changed, 173 insertions(+), 219 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 3dac8e0..843d42f 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -95,6 +95,72 @@ void PdmlDefaultProtocol::postProtocolHandler(OstProto::Stream *stream) return; // do nothing! } +void PdmlDefaultProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + google::protobuf::Message *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, stream); + } +} + +void PdmlDefaultProtocol::knownFieldHandler(QString name, QString valueHexStr, + google::protobuf::Message *pbProto) +{ + int fid = fieldId(name); + const google::protobuf::Descriptor *msgDesc = pbProto->GetDescriptor(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgDesc->FindFieldByNumber(fid); + const google::protobuf::Reflection *msgRefl = pbProto->GetReflection(); + + bool isOk; + + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(pbProto, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(pbProto, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(pbProto, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + void PdmlDefaultProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) @@ -120,10 +186,10 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); - factory_.insert("eth",PdmlEthProtocol::createInstance); - factory_.insert("ip",PdmlIp4Protocol::createInstance); - factory_.insert("ipv6",PdmlIp6Protocol::createInstance); - factory_.insert("tcp",PdmlTcpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); } PdmlReader::~PdmlReader() @@ -134,9 +200,8 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) { setDevice(device); pcap_ = pcap; - packetCount_ = 0; - stop_ = stop; + while (!atEnd()) { readNext(); @@ -153,7 +218,7 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) { qDebug("Line %lld", lineNumber()); qDebug("Col %lld", columnNumber()); - qDebug("%s", errorString().toAscii().constData()); // FIXME + qDebug("%s", errorString().toAscii().constData()); return false; } return true; @@ -162,6 +227,7 @@ bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) // TODO: use a temp pool to avoid a lot of new/delete PdmlDefaultProtocol* PdmlReader::allocPdmlProtocol(QString protoName) { + // If protoName is not known, we use a hexdump if (!factory_.contains(protoName)) protoName = "hexdump"; @@ -177,42 +243,24 @@ bool PdmlReader::isDontCareProto() { Q_ASSERT(isStartElement() && name() == "proto"); - QString protoName = attributes().value("name").toString(); + QStringRef protoName = attributes().value("name"); if (protoName.isEmpty() || (protoName == "expert")) return true; - else - return false; -} -void PdmlReader::readUnexpectedElement() -{ - Q_ASSERT(isStartElement()); - - // XXX: add to 'log' - qDebug("unexpected element - <%s>; skipping ...", - name().toString().toAscii().constData()); - while (!atEnd()) - { - readNext(); - if (isEndElement()) - break; - - if (isStartElement()) - readUnexpectedElement(); - } + return false; } void PdmlReader::skipElement() { Q_ASSERT(isStartElement()); - // XXX: add to 'log' qDebug("skipping element - <%s>", name().toString().toAscii().constData()); while (!atEnd()) { readNext(); + if (isEndElement()) break; @@ -230,6 +278,7 @@ void PdmlReader::readPdml() while (!atEnd()) { readNext(); + if (isEndElement()) break; @@ -238,7 +287,7 @@ void PdmlReader::readPdml() if (name() == "packet") readPacket(); else - readUnexpectedElement(); + skipElement(); } } } @@ -259,7 +308,7 @@ void PdmlReader::readPacket() currentStream_->mutable_stream_id()->set_id(packetCount_); currentStream_->mutable_core()->set_is_enabled(true); - // Set to a high number; will get reset to correct during parse + // Set to a high number; will get reset to correct value during parse currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! expPos_ = 0; @@ -283,32 +332,11 @@ void PdmlReader::readPacket() else if (name() == "field") readField(NULL, NULL); // TODO: top level field!!!! else - readUnexpectedElement(); + skipElement(); } } - // BAD Hack for TCP Segments - if (currentStream_->core().name().size()) -#if 0 - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - qDebug("Adding TCP Segment Data/FCS etc of size %d", - currentStream_->core().name().size()); - - hexDump->set_content(currentStream_->core().name()); - hexDump->set_pad_until_end(false); - currentStream_->mutable_core()->set_name(""); - - expPos_ += hexDump->content().size(); - } -#else - currentStream_->mutable_core()->set_name(""); -#endif + currentStream_->mutable_core()->set_name(""); // FIXME // If trailing bytes are missing, add those from the pcap if ((expPos_ < pktBuf_.size()) && pcap_) @@ -328,7 +356,7 @@ void PdmlReader::readPacket() } packetCount_++; - emit progress(int(characterOffset()*100/device()->size())); + emit progress(int(characterOffset()*100/device()->size())); // in % if (prevStream_) prevStream_->mutable_control()->CopyFrom(currentStream_->control()); if (stop_ && *stop_) @@ -342,10 +370,12 @@ void PdmlReader::readProto() Q_ASSERT(isStartElement() && name() == "proto"); - QString protoName = attributes().value("name").toString(); + QString protoName; int pos = -1; int size = -1; + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); if (!attributes().value("pos").isEmpty()) pos = attributes().value("pos").toString().toInt(); if (!attributes().value("size").isEmpty()) @@ -356,44 +386,27 @@ void PdmlReader::readProto() // This is a heuristic to skip protocols which are not part of // this frame, but of a reassembled segment spanning several frames - // 1. Proto starting pos is 0, but we already seen some protocols + // 1. Proto starting pos is 0, but we've already seen some protocols // 2. Protocol Size exceeds frame length if (((pos == 0) && (currentStream_->protocol_size() > 0)) || ((pos + size) > int(currentStream_->core().frame_len()))) { - qDebug("(skipped)"); skipElement(); return; } -#if 1 - if (protoName.isEmpty() || (protoName == "expert")) + if (isDontCareProto()) { skipElement(); return; } -#endif - // detect overlaps or gaps between subsequent protocols and "fill-in" + // if we detect a gap between subsequent protocols, we "fill-in" // with a "hexdump" from the pcap - if (pos >=0 && pcap_) + if (pos > expPos_ && pcap_) { - if (pos > expPos_) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - OstProto::HexDump *hexDump = proto->MutableExtension( - OstProto::hexDump); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - qDebug("filling in gap of %d bytes starting from %d", - pos - expPos_, expPos_); - hexDump->set_content(pktBuf_.constData() + expPos_, pos - expPos_); - hexDump->set_pad_until_end(false); - - expPos_ = pos; - } + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; } // for unknown protocol, read a hexdump from the pcap @@ -404,7 +417,6 @@ void PdmlReader::readProto() if (!attributes().value("size").isEmpty()) size = attributes().value("size").toString().toInt(); - // Check if this proto is a subset of previous proto - if so, do nothing if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) { @@ -416,47 +428,18 @@ void PdmlReader::readProto() if (pos >= 0 && size > 0 && ((pos + size) <= pktBuf_.size())) { - OstProto::Protocol *proto = currentStream_->add_protocol(); - OstProto::HexDump *hexDump = proto->MutableExtension( - OstProto::hexDump); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - qDebug("missing bytes - filling in %d bytes staring from %d", - size, pos); - hexDump->set_content(pktBuf_.constData() + pos, size); - hexDump->set_pad_until_end(false); + appendHexDumpProto(pos, size); + expPos_ += size; skipElement(); - - expPos_ += size; return; } } - pdmlProto = allocPdmlProtocol(protoName); - Q_ASSERT(pdmlProto != NULL); - - int protoId = pdmlProto->ostProtoId(); - - // Non PdmlDefaultProtocol - if (protoId > 0) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id(protoId); - - const google::protobuf::Reflection *msgRefl = proto->GetReflection(); - const google::protobuf::FieldDescriptor *fieldDesc = - msgRefl->FindKnownExtensionByNumber(protoId); - - // TODO: if !fDesc - // init default values of all fields in protocol - pbProto = msgRefl->MutableMessage(proto, fieldDesc); - - } + pdmlProto = appendPdmlProto(protoName, &pbProto); + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, currentStream_); @@ -471,23 +454,28 @@ void PdmlReader::readProto() { if (name() == "proto") { - int endPos = -1; // an embedded proto qDebug("embedded proto: %s\n", attributes().value("name") .toString().toAscii().constData()); + if (isDontCareProto()) { skipElement(); continue; } - if (!attributes().value("pos").isEmpty()) - endPos = attributes().value("pos").toString().toInt(); - - - // pdmlProto may be NULL for a sequence of embedded protos + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos if (pdmlProto) { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + pdmlProto->prematureEndHandler(endPos, currentStream_); pdmlProto->postProtocolHandler(currentStream_); @@ -495,7 +483,9 @@ void PdmlReader::readProto() s.protoDataCopyFrom(*currentStream_); expPos_ = s.frameProtocolLength(0); } + readProto(); + pdmlProto = NULL; pbProto = NULL; } @@ -511,38 +501,24 @@ void PdmlReader::readProto() continue; } - if (pdmlProto == NULL) { - pdmlProto = allocPdmlProtocol(protoName); - protoId = pdmlProto->ostProtoId(); + pdmlProto = appendPdmlProto(protoName, &pbProto); - // Non PdmlDefaultProtocol - if (protoId > 0) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id(protoId); - - const google::protobuf::Reflection *msgRefl = proto->GetReflection(); - const google::protobuf::FieldDescriptor *fieldDesc = - msgRefl->FindKnownExtensionByNumber(protoId); - - // TODO: if !fDesc - // init default values of all fields in protocol - pbProto = msgRefl->MutableMessage(proto, fieldDesc); - - } + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, currentStream_); } + readField(pdmlProto, pbProto); } else - readUnexpectedElement(); + skipElement(); } } + // Close-off current protocol if (pdmlProto) { pdmlProto->postProtocolHandler(currentStream_); @@ -567,61 +543,10 @@ void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, } QString fieldName = attributes().value("name").toString(); - QString valueHexStr = attributes().value("value").toString(); - int pos = -1; - int size = -1; - if (!attributes().value("pos").isEmpty()) - pos = attributes().value("pos").toString().toInt(); - if (!attributes().value("size").isEmpty()) - size = attributes().value("size").toString().toInt(); + qDebug(" fieldName:%s", fieldName.toAscii().constData()); - qDebug("\tfieldName:%s, pos:%d, size:%d value:%s", - fieldName.toAscii().constData(), - pos, - size, - valueHexStr.toAscii().constData()); - - if (pdmlProto->hasField(fieldName)) - { - int fieldId = pdmlProto->fieldId(fieldName); - const google::protobuf::Descriptor *msgDesc = - pbProto->GetDescriptor(); - const google::protobuf::FieldDescriptor *fieldDesc = - msgDesc->FindFieldByNumber(fieldId); - const google::protobuf::Reflection *msgRefl = - pbProto->GetReflection(); - - bool isOk; - - switch(fieldDesc->cpp_type()) - { - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO - case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: - msgRefl->SetUInt32(pbProto, fieldDesc, - valueHexStr.toUInt(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: - msgRefl->SetUInt64(pbProto, fieldDesc, - valueHexStr.toULongLong(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_STRING: - { - QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); - std::string str(hexVal.constData(), hexVal.size()); - msgRefl->SetString(pbProto, fieldDesc, str); - break; - } - default: - qDebug("%s: unhandled cpptype = %d", __FUNCTION__, - fieldDesc->cpp_type()); - } - } - else - { - pdmlProto->unknownFieldHandler(fieldName, pos, size, attributes(), - currentStream_); - } + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); while (!atEnd()) { @@ -633,22 +558,56 @@ void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, if (isStartElement()) { if (name() == "proto") - { - if (isDontCareProto()) - { - skipElement(); - continue; - } readProto(); - } else if (name() == "field") readField(pdmlProto, pbProto); else - readUnexpectedElement(); + skipElement(); } } } +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlDefaultProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + google::protobuf::Message **pbProto) +{ + PdmlDefaultProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + *pbProto = msgRefl->MutableMessage(proto, fieldDesc); + } + else + *pbProto = NULL; + + return pdmlProto; +} + // ---------------------------------------------------------- // // PdmlUnknownProtocol // @@ -720,9 +679,6 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) } qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); - //Q_ASSERT(expPos_ == endPos_); - - hexDump->set_pad_until_end(false); // If empty for some reason, remove the protocol if (hexDump->content().size() == 0) @@ -750,7 +706,7 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, expPos_ += hexVal.size(); } - if ((pos == expPos_) /*&& (pos < endPos_)*/) + if (pos == expPos_) { QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : diff --git a/common/pdml_p.h b/common/pdml_p.h index af7c4f6..614a4cc 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -23,16 +23,12 @@ along with this program. If not, see #include #include -#include #include #include #include // TODO: add const where possible -class QXmlSimpleReader; -class QXmlInputSource; - class PdmlDefaultProtocol { public: @@ -51,6 +47,11 @@ public: int expectedPos, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + google::protobuf::Message *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + google::protobuf::Message *pbProto); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream); @@ -65,7 +66,7 @@ class PcapFileFormat; class PdmlReader : public QObject, public QXmlStreamReader { Q_OBJECT - friend class PdmlUnknownProtocol; + //friend class PdmlUnknownProtocol; public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); @@ -80,34 +81,32 @@ private: void freePdmlProtocol(PdmlDefaultProtocol *proto); bool isDontCareProto(); - void readPdml(); void skipElement(); - void readUnexpectedElement(); - - void readPacketPass1(); - void readProtoPass1(); - void readFieldPass1(); + void readPdml(); void readPacket(); void readProto(); void readField(PdmlDefaultProtocol *pdmlProto, google::protobuf::Message *pbProto); + void appendHexDumpProto(int offset, int size); + PdmlDefaultProtocol* appendPdmlProto(const QString &protoName, + google::protobuf::Message **pbProto); + typedef PdmlDefaultProtocol* (*FactoryMethod)(); QMap factory_; + bool *stop_; OstProto::StreamConfigList *streams_; PcapFileFormat *pcap_; + QByteArray pktBuf_; int packetCount_; int expPos_; bool skipUntilEnd_; OstProto::Stream *prevStream_; OstProto::Stream *currentStream_; - - QByteArray pktBuf_; - bool *stop_; }; class PdmlUnknownProtocol : public PdmlDefaultProtocol @@ -135,7 +134,6 @@ public: PdmlGenInfoProtocol(); static PdmlDefaultProtocol* createInstance(); - }; class PdmlFrameProtocol : public PdmlDefaultProtocol From 873daa96045231afb8ec86d09e1268286f0758aa Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 26 Mar 2011 21:36:57 +0530 Subject: [PATCH 139/294] Implemented PDML Export --- common/pdmlfileformat.cpp | 52 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 1e2c4c8..0f85542 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -18,8 +18,13 @@ along with this program. If not, see */ #include "pdmlfileformat.h" + +#include "ostprotolib.h" #include "pdml_p.h" +#include +#include + PdmlFileFormat pdmlFileFormat; PdmlFileFormat::PdmlFileFormat() @@ -62,8 +67,51 @@ _exit: bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { - error = "Save to PDML format is not supported"; - return false; + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; } bool PdmlFileFormat::isMyFileFormat(const QString fileName) From b8e451574ad766d66cc029b99aa9e690e258aa90 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 27 Mar 2011 18:19:31 +0530 Subject: [PATCH 140/294] another round of code cleanup --- common/pdml_p.cpp | 127 ++++++++++++++++++++++++++-------------------- common/pdml_p.h | 58 +++++++++++++-------- 2 files changed, 108 insertions(+), 77 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 843d42f..6b77d19 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -80,24 +80,27 @@ int PdmlDefaultProtocol::fieldId(QString name) const void PdmlDefaultProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, - int /*expectedPos*/, OstProto::Stream* /*stream*/) + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) { return; // do nothing! } -void PdmlDefaultProtocol::prematureEndHandler(int pos, OstProto::Stream *stream) +void PdmlDefaultProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } -void PdmlDefaultProtocol::postProtocolHandler(OstProto::Stream *stream) +void PdmlDefaultProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) { return; // do nothing! } void PdmlDefaultProtocol::fieldHandler(QString name, const QXmlStreamAttributes &attributes, - google::protobuf::Message *pbProto, OstProto::Stream *stream) + OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (hasField(name)) { @@ -122,37 +125,43 @@ void PdmlDefaultProtocol::fieldHandler(QString name, qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", name.toAscii().constData(), pos, size); - unknownFieldHandler(name, pos, size, attributes, stream); + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); } } void PdmlDefaultProtocol::knownFieldHandler(QString name, QString valueHexStr, - google::protobuf::Message *pbProto) + OstProto::Protocol *pbProto) { - int fid = fieldId(name); - const google::protobuf::Descriptor *msgDesc = pbProto->GetDescriptor(); + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); const google::protobuf::FieldDescriptor *fieldDesc = - msgDesc->FindFieldByNumber(fid); - const google::protobuf::Reflection *msgRefl = pbProto->GetReflection(); + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); bool isOk; + Q_ASSERT(fieldDesc != NULL); switch(fieldDesc->cpp_type()) { case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: - msgRefl->SetUInt32(pbProto, fieldDesc, + msgRefl->SetUInt32(msg, fieldDesc, valueHexStr.toUInt(&isOk, kBaseHex)); break; case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: - msgRefl->SetUInt64(pbProto, fieldDesc, + msgRefl->SetUInt64(msg, fieldDesc, valueHexStr.toULongLong(&isOk, kBaseHex)); break; case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); std::string str(hexVal.constData(), hexVal.size()); - msgRefl->SetString(pbProto, fieldDesc, str); + msgRefl->SetString(msg, fieldDesc, str); break; } default: @@ -163,7 +172,7 @@ void PdmlDefaultProtocol::knownFieldHandler(QString name, QString valueHexStr, void PdmlDefaultProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, - OstProto::Stream *stream) + OstProto::Protocol *pbProto, OstProto::Stream *stream) { return; // do nothing! } @@ -366,7 +375,7 @@ void PdmlReader::readPacket() void PdmlReader::readProto() { PdmlDefaultProtocol *pdmlProto = NULL; - google::protobuf::Message *pbProto = NULL; + OstProto::Protocol *pbProto = NULL; Q_ASSERT(isStartElement() && name() == "proto"); @@ -440,7 +449,7 @@ void PdmlReader::readProto() qDebug("%s: preProtocolHandler(expPos = %d)", protoName.toAscii().constData(), expPos_); - pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, currentStream_); while (!atEnd()) @@ -476,8 +485,9 @@ void PdmlReader::readProto() if (!attributes().value("pos").isEmpty()) endPos = attributes().value("pos").toString().toInt(); - pdmlProto->prematureEndHandler(endPos, currentStream_); - pdmlProto->postProtocolHandler(currentStream_); + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); StreamBase s; s.protoDataCopyFrom(*currentStream_); @@ -508,7 +518,7 @@ void PdmlReader::readProto() qDebug("%s: preProtocolHandler(expPos = %d)", protoName.toAscii().constData(), expPos_); pdmlProto->preProtocolHandler(protoName, attributes(), - expPos_, currentStream_); + expPos_, pbProto, currentStream_); } readField(pdmlProto, pbProto); @@ -521,7 +531,7 @@ void PdmlReader::readProto() // Close-off current protocol if (pdmlProto) { - pdmlProto->postProtocolHandler(currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); freePdmlProtocol(pdmlProto); StreamBase s; @@ -531,7 +541,7 @@ void PdmlReader::readProto() } void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, - google::protobuf::Message *pbProto) + OstProto::Protocol *pbProto) { Q_ASSERT(isStartElement() && name() == "field"); @@ -581,7 +591,7 @@ void PdmlReader::appendHexDumpProto(int offset, int size) } PdmlDefaultProtocol* PdmlReader::appendPdmlProto(const QString &protoName, - google::protobuf::Message **pbProto) + OstProto::Protocol **pbProto) { PdmlDefaultProtocol* pdmlProto = allocPdmlProtocol(protoName); Q_ASSERT(pdmlProto != NULL); @@ -600,7 +610,12 @@ PdmlDefaultProtocol* PdmlReader::appendPdmlProto(const QString &protoName, // TODO: if !fDesc // init default values of all fields in protocol - *pbProto = msgRefl->MutableMessage(proto, fieldDesc); + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); } else *pbProto = NULL; @@ -627,8 +642,8 @@ PdmlDefaultProtocol* PdmlUnknownProtocol::createInstance() } void PdmlUnknownProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { bool isOk; int size; @@ -659,15 +674,16 @@ _skip_pos_size_proc: hexDump->set_pad_until_end(false); } -void PdmlUnknownProtocol::prematureEndHandler(int pos, OstProto::Stream *stream) +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { endPos_ = pos; } -void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) { - OstProto::HexDump *hexDump = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); // Skipped field(s) at end? Pad with zero! if (endPos_ > expPos_) @@ -688,10 +704,10 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Stream *stream) } void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { - OstProto::HexDump *hexDump = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", name.toAscii().constData(), @@ -755,8 +771,9 @@ PdmlDefaultProtocol* PdmlFrameProtocol::createInstance() return new PdmlFrameProtocol(); } -void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, - int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) +void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (name == "frame.len") { @@ -811,7 +828,8 @@ PdmlDefaultProtocol* PdmlEthProtocol::createInstance() } void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (name == "eth.type") { @@ -887,7 +905,8 @@ PdmlDefaultProtocol* PdmlIp4Protocol::createInstance() } void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { bool isOk; @@ -899,17 +918,16 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, } else if (name == "ip.flags") { - OstProto::Ip4 *ip4 = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::ip4); + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); } } -void PdmlIp4Protocol::postProtocolHandler(OstProto::Stream *stream) +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) { - OstProto::Ip4 *ip4 = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::ip4); + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); ip4->set_is_override_ver(true); // FIXME ip4->set_is_override_hdrlen(true); // FIXME @@ -958,14 +976,14 @@ PdmlDefaultProtocol* PdmlIp6Protocol::createInstance() } void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) { bool isOk; if (name == "ipv6.src") { - OstProto::Ip6 *ip6 = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); QString addrHexStr = attributes.value("value").toString(); ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); @@ -973,8 +991,7 @@ void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, } else if (name == "ipv6.dst") { - OstProto::Ip6 *ip6 = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); QString addrHexStr = attributes.value("value").toString(); ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); @@ -982,10 +999,10 @@ void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, } } -void PdmlIp6Protocol::postProtocolHandler(OstProto::Stream *stream) +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) { - OstProto::Ip6 *ip6 = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::ip6); + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); ip6->set_is_override_version(true); // FIXME ip6->set_is_override_payload_length(true); // FIXME @@ -1018,7 +1035,8 @@ PdmlDefaultProtocol* PdmlTcpProtocol::createInstance() } void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream) + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) { if (name == "tcp.options") options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); @@ -1033,18 +1051,17 @@ void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, else if (attributes.value("show").toString().startsWith("Acknowledgement number")) { bool isOk; - OstProto::Tcp *tcp = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::tcp); + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); } } } -void PdmlTcpProtocol::postProtocolHandler(OstProto::Stream *stream) +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) { - OstProto::Tcp *tcp = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::tcp); + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); qDebug("Tcp: post\n"); diff --git a/common/pdml_p.h b/common/pdml_p.h index 614a4cc..4c1b639 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -43,17 +43,20 @@ public: int fieldId(QString name) const; virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Stream *stream); - virtual void prematureEndHandler(int pos, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); void fieldHandler(QString name, const QXmlStreamAttributes &attributes, - google::protobuf::Message *pbProto, OstProto::Stream *stream); + OstProto::Protocol *pbProto, OstProto::Stream *stream); void knownFieldHandler(QString name, QString valueHexStr, - google::protobuf::Message *pbProto); + OstProto::Protocol *pbProto); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ @@ -87,11 +90,11 @@ private: void readPacket(); void readProto(); void readField(PdmlDefaultProtocol *pdmlProto, - google::protobuf::Message *pbProto); + OstProto::Protocol *pbProto); void appendHexDumpProto(int offset, int size); PdmlDefaultProtocol* appendPdmlProto(const QString &protoName, - google::protobuf::Message **pbProto); + OstProto::Protocol **pbProto); typedef PdmlDefaultProtocol* (*FactoryMethod)(); @@ -117,12 +120,15 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Stream *stream); - virtual void prematureEndHandler(int pos, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); private: int endPos_; int expPos_; @@ -144,7 +150,8 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); }; class PdmlEthProtocol : public PdmlDefaultProtocol @@ -155,7 +162,8 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); }; class PdmlIp4Protocol : public PdmlDefaultProtocol @@ -166,8 +174,10 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); private: QByteArray options_; }; @@ -180,8 +190,10 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); }; class PdmlTcpProtocol : public PdmlDefaultProtocol @@ -192,8 +204,10 @@ public: static PdmlDefaultProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Stream *stream); + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); private: QByteArray options_; QByteArray segmentData_; From 28cd7178951ec89a1c075fcb02b715f4d3da4d38 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 27 Mar 2011 22:45:04 +0530 Subject: [PATCH 141/294] Implemented PDML LLC Protocol (including SNAP). Also implemented PDML VLAN Protocol - but this is not working so is disabled for the moment --- common/pdml_p.cpp | 138 +++++++++++++++++++++++++++++++++++++++++++++- common/pdml_p.h | 29 ++++++++++ 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 6b77d19..b2e3134 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -24,17 +24,22 @@ along with this program. If not, see #include "protocolmanager.h" #include "streambase.h" -#include "mac.pb.h" #include "eth2.pb.h" #include "dot3.pb.h" #include "hexdump.pb.h" +#include "llc.pb.h" +#include "mac.pb.h" #include "ip4.pb.h" #include "ip6.pb.h" +#include "snap.pb.h" +#include "svlan.pb.h" #include "tcp.pb.h" +#include "vlan.pb.h" #include #include +#include #include @@ -195,10 +200,13 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); + //factory_.insert("vlan", PdmlVlanProtocol::createInstance); } PdmlReader::~PdmlReader() @@ -809,6 +817,63 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, int size, } } + +// ---------------------------------------------------------- // +// PdmlVlanProtocol // +// ---------------------------------------------------------- // + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + pdmlProtoName_ = "vlan"; + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlDefaultProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + // ---------------------------------------------------------- // // PdmlEthProtocol // // ---------------------------------------------------------- // @@ -833,6 +898,14 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, { if (name == "eth.type") { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + if ((type == 0x88a8) || (type == 0x8100)) + return; + OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( @@ -840,8 +913,7 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); - bool isOk; - eth2->set_type(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + eth2->set_type(type); eth2->set_is_override_type(true); } else if (name == "eth.len") @@ -877,6 +949,66 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, } +// ---------------------------------------------------------- // +// PdmlLlcProtocol // +// ---------------------------------------------------------- // + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + pdmlProtoName_ = "llc"; + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlDefaultProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + + // ---------------------------------------------------------- // // PdmlIp4Protocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index 4c1b639..d1e5cea 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -166,6 +166,35 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); }; +class PdmlVlanProtocol : public PdmlDefaultProtocol +{ +public: + PdmlVlanProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +}; + +class PdmlLlcProtocol : public PdmlDefaultProtocol +{ +public: + PdmlLlcProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +}; + class PdmlIp4Protocol : public PdmlDefaultProtocol { public: From 137b49594a4f6b44ef299b8ec4da5c046e7b9f95 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 28 Mar 2011 22:23:33 +0530 Subject: [PATCH 142/294] Fixed Vlan PDML Protocol --- common/pdml_p.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index b2e3134..6b2bfb7 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -206,7 +206,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); - //factory_.insert("vlan", PdmlVlanProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); } PdmlReader::~PdmlReader() @@ -841,6 +841,23 @@ void PdmlVlanProtocol::preProtocolHandler(QString name, vlan->set_tpid(0x8100); vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } } void PdmlVlanProtocol::unknownFieldHandler(QString name, int pos, int size, @@ -903,9 +920,6 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, uint type = attributes.value("value").toString() .toUInt(&isOk, kBaseHex); - if ((type == 0x88a8) || (type == 0x8100)) - return; - OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( From 0347b939b7d01f001b9d02faf5f5a060a67d4f7a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 29 Mar 2011 21:17:37 +0530 Subject: [PATCH 143/294] Implemented SVlan (IEEE 802.1ad) PDML Protocol --- common/pdml_p.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++ common/pdml_p.h | 15 ++++++++ 2 files changed, 108 insertions(+) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 6b2bfb7..12e4b54 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -205,6 +205,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); factory_.insert("vlan", PdmlVlanProtocol::createInstance); } @@ -818,6 +819,98 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, int size, } +// ---------------------------------------------------------- // +// PdmlSvlanProtocol // +// ---------------------------------------------------------- // + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + pdmlProtoName_ = "ieee8021ad"; + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlDefaultProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + // ---------------------------------------------------------- // // PdmlVlanProtocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index d1e5cea..7f2a54b 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -166,6 +166,21 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); }; +class PdmlSvlanProtocol : public PdmlDefaultProtocol +{ +public: + PdmlSvlanProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +}; + class PdmlVlanProtocol : public PdmlDefaultProtocol { public: From a7d28d8a8d2f29591ac1dee5c82bb6b569b9cca7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 31 Mar 2011 20:36:47 +0530 Subject: [PATCH 144/294] Implemented ARP PDML Protocol. Removed hardcoded field numbers from IPv4 --- common/pdml_p.cpp | 48 ++++++++++++++++++++++++++++++++++++----------- common/pdml_p.h | 8 ++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 12e4b54..dc9cc8d 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -24,6 +24,7 @@ along with this program. If not, see #include "protocolmanager.h" #include "streambase.h" +#include "arp.pb.h" #include "eth2.pb.h" #include "dot3.pb.h" #include "hexdump.pb.h" @@ -203,6 +204,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("arp", PdmlArpProtocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); @@ -1116,6 +1118,30 @@ void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, } +// ---------------------------------------------------------- // +// PdmlArpProtocol // +// ---------------------------------------------------------- // + +PdmlArpProtocol::PdmlArpProtocol() +{ + pdmlProtoName_ = "arp"; + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlDefaultProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + + // ---------------------------------------------------------- // // PdmlIp4Protocol // // ---------------------------------------------------------- // @@ -1125,17 +1151,17 @@ PdmlIp4Protocol::PdmlIp4Protocol() pdmlProtoName_ = "ip"; ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; - fieldMap_.insert("ip.version", 5); - fieldMap_.insert("ip.dsfield", 6); - fieldMap_.insert("ip.len", 7); - fieldMap_.insert("ip.id", 8); - //fieldMap_.insert("ip.flags", 9); - fieldMap_.insert("ip.frag_offset", 10); - fieldMap_.insert("ip.ttl", 11); - fieldMap_.insert("ip.proto", 12); - fieldMap_.insert("ip.checksum", 13); - fieldMap_.insert("ip.src", 14); - fieldMap_.insert("ip.dst", 18); + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); } PdmlDefaultProtocol* PdmlIp4Protocol::createInstance() diff --git a/common/pdml_p.h b/common/pdml_p.h index 7f2a54b..2220898 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -210,6 +210,14 @@ public: OstProto::Stream *stream); }; +class PdmlArpProtocol : public PdmlDefaultProtocol +{ +public: + PdmlArpProtocol(); + + static PdmlDefaultProtocol* createInstance(); +}; + class PdmlIp4Protocol : public PdmlDefaultProtocol { public: From e47319fedaccb4b8b344653ba41588dee31d6f18 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 31 Mar 2011 21:04:05 +0530 Subject: [PATCH 145/294] Implemented UDP (and UDPLite) PDML Protocol --- common/pdml_p.cpp | 38 ++++++++++++++++++++++++++++++++++++++ common/pdml_p.h | 10 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index dc9cc8d..a639901 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -35,6 +35,7 @@ along with this program. If not, see #include "snap.pb.h" #include "svlan.pb.h" #include "tcp.pb.h" +#include "udp.pb.h" #include "vlan.pb.h" #include @@ -209,6 +210,8 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); factory_.insert("vlan", PdmlVlanProtocol::createInstance); } @@ -1368,3 +1371,38 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, #endif } +// ---------------------------------------------------------- // +// PdmlUdpProtocol // +// ---------------------------------------------------------- // + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + pdmlProtoName_ = "udp"; // OR udplite + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlDefaultProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + diff --git a/common/pdml_p.h b/common/pdml_p.h index 2220898..94de053 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -265,4 +265,14 @@ private: QByteArray segmentData_; }; +class PdmlUdpProtocol : public PdmlDefaultProtocol +{ +public: + PdmlUdpProtocol(); + + static PdmlDefaultProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +}; + #endif From ead67d80aabfeee0e451ea53b1bd3915d6cb53fe Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 2 Apr 2011 00:14:43 +0530 Subject: [PATCH 146/294] Finished implementing PDML Text Protocol - verified for HTTP --- common/ip4.cpp | 2 +- common/pdml_p.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++-- common/pdml_p.h | 27 ++++++++++ 3 files changed, 149 insertions(+), 4 deletions(-) diff --git a/common/ip4.cpp b/common/ip4.cpp index 4cc789b..197cc8e 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -724,7 +724,7 @@ void Ip4Protocol::storeConfigWidget() data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); - data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk)); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk, 16)); data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index a639901..063dd63 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -35,6 +35,7 @@ along with this program. If not, see #include "snap.pb.h" #include "svlan.pb.h" #include "tcp.pb.h" +#include "textproto.pb.h" #include "udp.pb.h" #include "vlan.pb.h" @@ -203,12 +204,16 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); - factory_.insert("eth", PdmlEthProtocol::createInstance); - factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); - factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + //factory_.insert("nntp", PdmlTextProtocol::createInstance); + //factory_.insert("rtsp", PdmlTextProtocol::createInstance); + //factory_.insert("sip", PdmlTextProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); factory_.insert("udp", PdmlUdpProtocol::createInstance); factory_.insert("udplite", PdmlUdpProtocol::createInstance); @@ -1406,3 +1411,116 @@ void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, udp->set_is_override_cksum(true); } + +// ---------------------------------------------------------- // +// PdmlTextProtocol // +// ---------------------------------------------------------- // + +PdmlTextProtocol::PdmlTextProtocol() +{ + pdmlProtoName_ = "text"; + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlDefaultProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); + + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (contentType_ == kUnknownContent) + { + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + } + else if (contentType_ == kOtherContent) + return; + else // kTextContent + { + if (name == "data") + contentType_ = kOtherContent; + return; + } + + // We process only kTextContent + + if (pos != expPos_) + return; + + if ((pos + size) > endPos_) + return; + + if (attributes.value("show") == "HTTP chunked response") + return; + + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + // Convert line endings to LF only - Qt requirement that TextProto honours + line.replace("\r\n", "\n"); + line.replace("\r", "\n"); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + contentType_ = kUnknownContent; +} + diff --git a/common/pdml_p.h b/common/pdml_p.h index 94de053..661c8c8 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -275,4 +275,31 @@ public: OstProto::Stream *stream); }; +class PdmlTextProtocol : public PdmlDefaultProtocol +{ +public: + PdmlTextProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + ContentType contentType_; + int expPos_; + int endPos_; +}; + #endif From 610ee312a0bad8cc6c27dc12f7ded531033c10c7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 3 Apr 2011 14:13:57 +0530 Subject: [PATCH 147/294] snapshot before rewriting PDML TextProto --- common/pdml_p.cpp | 60 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 063dd63..f76e116 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -211,9 +211,9 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); - //factory_.insert("nntp", PdmlTextProtocol::createInstance); - //factory_.insert("rtsp", PdmlTextProtocol::createInstance); - //factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); factory_.insert("udp", PdmlUdpProtocol::createInstance); factory_.insert("udplite", PdmlUdpProtocol::createInstance); @@ -409,8 +409,8 @@ void PdmlReader::readProto() if (!attributes().value("size").isEmpty()) size = attributes().value("size").toString().toInt(); - qDebug("proto: %s, pos = %d, expPos_ = %d", - protoName.toAscii().constData(), pos, expPos_); + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); // This is a heuristic to skip protocols which are not part of // this frame, but of a reassembled segment spanning several frames @@ -1456,6 +1456,7 @@ void PdmlTextProtocol::preProtocolHandler(QString name, endPos_ = expPos_ + size; _skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); OstProto::TextProtocol *text = pbProto->MutableExtension( OstProto::textProtocol); @@ -1469,30 +1470,49 @@ void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { + if (name == "data") + contentType_ = kOtherContent; + + if (contentType_ == kOtherContent) + return; + if (contentType_ == kUnknownContent) - { - if (name == "data") - contentType_ = kOtherContent; - else - contentType_ = kTextContent; - } - else if (contentType_ == kOtherContent) - return; - else // kTextContent - { - if (name == "data") - contentType_ = kOtherContent; - return; - } + contentType_ = kTextContent; // We process only kTextContent - if (pos != expPos_) + if (pos < expPos_) return; if ((pos + size) > endPos_) return; + if (pos > expPos_) + { + int gap = pos - expPos_; + QString eol; + QByteArray filler; + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + switch(text->eol()) + { + case OstProto::TextProtocol::kCr: eol = "\r"; break; + case OstProto::TextProtocol::kLf: eol = "\n"; break; + case OstProto::TextProtocol::kCrLf: eol = "\r\n"; break; + default: Q_ASSERT(false); + } + eol = "\n"; + + for (int i = 0; i < gap; i++) + filler += eol; + + filler.resize(gap); + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + if (attributes.value("show") == "HTTP chunked response") return; From cbcd9872d479fa0c09b15e67b50f6bd065d48a47 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 3 Apr 2011 20:49:57 +0530 Subject: [PATCH 148/294] Reworked PDML Text Protocol --- common/pdml_p.cpp | 143 ++++++++++++++++++++++++++++++---------------- common/pdml_p.h | 1 + 2 files changed, 95 insertions(+), 49 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index f76e116..50c5e2b 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -208,12 +208,16 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("http", PdmlTextProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); factory_.insert("udp", PdmlUdpProtocol::createInstance); factory_.insert("udplite", PdmlUdpProtocol::createInstance); @@ -587,7 +591,26 @@ void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, if (isStartElement()) { if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + readProto(); + } else if (name() == "field") readField(pdmlProto, pbProto); else @@ -1461,8 +1484,9 @@ _skip_pos_size_proc: OstProto::textProtocol); text->set_port_num(0); - text->set_eol(OstProto::TextProtocol::kCrLf); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + detectEol_ = true; contentType_ = kUnknownContent; } @@ -1470,64 +1494,84 @@ void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { - if (name == "data") - contentType_ = kOtherContent; +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; - if (contentType_ == kOtherContent) - return; - - if (contentType_ == kUnknownContent) - contentType_ = kTextContent; - - // We process only kTextContent - - if (pos < expPos_) - return; - - if ((pos + size) > endPos_) - return; - - if (pos > expPos_) - { - int gap = pos - expPos_; - QString eol; - QByteArray filler; + case kTextContent: + { OstProto::TextProtocol *text = pbProto->MutableExtension( OstProto::textProtocol); - switch(text->eol()) + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) { - case OstProto::TextProtocol::kCr: eol = "\r"; break; - case OstProto::TextProtocol::kLf: eol = "\n"; break; - case OstProto::TextProtocol::kCrLf: eol = "\r\n"; break; - default: Q_ASSERT(false); + contentType_ = kOtherContent; + goto _retry; } - eol = "\n"; - for (int i = 0; i < gap; i++) - filler += eol; + if (pos < expPos_) + break; - filler.resize(gap); + if ((pos + size) > endPos_) + break; - text->mutable_text()->append(filler.constData(), filler.size()); - expPos_ += gap; + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); } - - if (attributes.value("show") == "HTTP chunked response") - return; - - OstProto::TextProtocol *text = pbProto->MutableExtension( - OstProto::textProtocol); - - QByteArray line = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - - // Convert line endings to LF only - Qt requirement that TextProto honours - line.replace("\r\n", "\n"); - line.replace("\r", "\n"); - - text->mutable_text()->append(line.constData(), line.size()); - expPos_ += size; } void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, @@ -1541,6 +1585,7 @@ void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, stream->mutable_protocol()->RemoveLast(); expPos_ = endPos_ = -1; + detectEol_ = true; contentType_ = kUnknownContent; } diff --git a/common/pdml_p.h b/common/pdml_p.h index 661c8c8..86b0d1c 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -297,6 +297,7 @@ private: kOtherContent }; + bool detectEol_; ContentType contentType_; int expPos_; int endPos_; From b230ff8082eb2acc608c32498e70581cecf604bf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 4 Apr 2011 14:26:46 +0530 Subject: [PATCH 149/294] Implemented ICMPv4/ICMPv6 PDML Protocol --- common/pdml_p.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++ common/pdml_p.h | 22 ++++++++++++++ common/udp.cpp | 4 +-- 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 50c5e2b..934d685 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -30,6 +30,7 @@ along with this program. If not, see #include "hexdump.pb.h" #include "llc.pb.h" #include "mac.pb.h" +#include "icmp.pb.h" #include "ip4.pb.h" #include "ip6.pb.h" #include "snap.pb.h" @@ -207,6 +208,8 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("arp", PdmlArpProtocol::createInstance); factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmpProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("imap", PdmlTextProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); @@ -1305,6 +1308,80 @@ void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, ip6->set_is_override_next_header(true); // FIXME } + +// ---------------------------------------------------------- // +// PdmlIcmpProtocol // +// ---------------------------------------------------------- // + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + pdmlProtoName_ = "icmp"; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + // ICMPv6 ident and seq need to be handled as 'unknown' since Wireshark + // doesn't define display filters for the same +} + +PdmlDefaultProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + + // ---------------------------------------------------------- // // PdmlTcpProtocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index 86b0d1c..ae3032e 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -248,6 +248,28 @@ public: OstProto::Stream *stream); }; +class PdmlIcmpProtocol : public PdmlDefaultProtocol +{ +public: + PdmlIcmpProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + class PdmlTcpProtocol : public PdmlDefaultProtocol { public: diff --git a/common/udp.cpp b/common/udp.cpp index 8f3c35b..8856751 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -484,8 +484,8 @@ void UdpProtocol::storeConfigWidget() setFieldData(udp_isOverrideTotLen, configForm->cbUdpLengthOverride->isChecked()); - setFieldData(udp_cksum, configForm->leUdpCksum->text().toUInt( - &isOk, BASE_HEX)); + setFieldData(udp_cksum, configForm->leUdpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); setFieldData(udp_isOverrideCksum, configForm->cbUdpCksumOverride->isChecked()); } From bda2d48adbc00e9fe3e3fbe3f0d5de9b8f349f93 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 4 Apr 2011 21:46:35 +0530 Subject: [PATCH 150/294] Implemented IGMP PDML protocol --- common/pdml_p.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++++ common/pdml_p.h | 25 +++++++++ 2 files changed, 152 insertions(+) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 934d685..0ab19af 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -31,6 +31,7 @@ along with this program. If not, see #include "llc.pb.h" #include "mac.pb.h" #include "icmp.pb.h" +#include "igmp.pb.h" #include "ip4.pb.h" #include "ip6.pb.h" #include "snap.pb.h" @@ -157,6 +158,9 @@ void PdmlDefaultProtocol::knownFieldHandler(QString name, QString valueHexStr, Q_ASSERT(fieldDesc != NULL); switch(fieldDesc->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: msgRefl->SetUInt32(msg, fieldDesc, @@ -210,6 +214,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("http", PdmlTextProtocol::createInstance); factory_.insert("icmp", PdmlIcmpProtocol::createInstance); factory_.insert("icmpv6", PdmlIcmpProtocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("imap", PdmlTextProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); @@ -1382,6 +1387,128 @@ void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, } +// ---------------------------------------------------------- // +// PdmlIgmpProtocol // +// ---------------------------------------------------------- // + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + pdmlProtoName_ = "igmp"; + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlDefaultProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + + // ---------------------------------------------------------- // // PdmlTcpProtocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index ae3032e..5c557ed 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -270,6 +270,31 @@ private: static const uint kIcmp6EchoReply = 129; }; +class PdmlIgmpProtocol : public PdmlDefaultProtocol +{ +public: + PdmlIgmpProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; + +}; + class PdmlTcpProtocol : public PdmlDefaultProtocol { public: From 41738b3e08dc1813ae71fd5af81b252dcbbf22cf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 5 Apr 2011 19:09:59 +0530 Subject: [PATCH 151/294] Modified test code so that duplicate diff is not done --- test/main.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/test/main.cpp b/test/main.cpp index 94ff561..c48a9e3 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -15,9 +15,9 @@ int main(int argc, char* argv[]) QCoreApplication app(argc, argv); QString error; - if (argc != 3) + if (argc != 2) { - printf("%s \n", argv[0]); + printf("%s \n", argv[0]); exit(255); } @@ -25,7 +25,6 @@ int main(int argc, char* argv[]) OstProto::StreamConfigList streams; QString inFile(argv[1]); - QString outFile(argv[2]); OstProtocolManager = new ProtocolManager(); @@ -38,20 +37,10 @@ int main(int argc, char* argv[]) isOk = pcapFileFormat.openStreams(inFile, streams, error); if (!error.isEmpty()) { - fprintf(stdout, "failed reading streams from %s:%s\n", + printf("%s: %s\n", inFile.toAscii().constData(), error.toAscii().constData()); } - if (!isOk) - exit(1); - - isOk = pcapFileFormat.saveStreams(streams, outFile, error); - if (!error.isEmpty()) - { - fprintf(stdout, "failed writing streams to %s:%s\n", - outFile.toAscii().constData(), error.toAscii().constData()); - } - if (!isOk) exit(1); From f132e3118c40f4d19212fbdc6bcd2367a92ce383 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 8 Apr 2011 20:44:32 +0530 Subject: [PATCH 152/294] Implemented MLD PDML Protocol - also ICMPv6 PDML protocol which detects and hands over control to either the ICMP PDML Protocol or MLD PDML Protocol --- common/mld.cpp | 2 +- common/pdml_p.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++-- common/pdml_p.h | 46 ++++++++- 3 files changed, 286 insertions(+), 8 deletions(-) diff --git a/common/mld.cpp b/common/mld.cpp index 98feab5..4c373ae 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -385,7 +385,7 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, qToBigEndian(rec.sources(j).v6_hi(), (uchar*)(rv.data()+20+16*j)); qToBigEndian(rec.sources(j).v6_lo(), - (uchar*)(rv.data()+20+16*j)); + (uchar*)(rv.data()+20+16*j+8)); } fv.append(rv); diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 0ab19af..42973e0 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -34,6 +34,8 @@ along with this program. If not, see #include "igmp.pb.h" #include "ip4.pb.h" #include "ip6.pb.h" +#include "mld.pb.h" +#include "sample.pb.h" #include "snap.pb.h" #include "svlan.pb.h" #include "tcp.pb.h" @@ -213,7 +215,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("http", PdmlTextProtocol::createInstance); factory_.insert("icmp", PdmlIcmpProtocol::createInstance); - factory_.insert("icmpv6", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); factory_.insert("igmp", PdmlIgmpProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("imap", PdmlTextProtocol::createInstance); @@ -1047,7 +1049,37 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { - if (name == "eth.type") + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") { bool isOk; @@ -1332,8 +1364,10 @@ PdmlIcmpProtocol::PdmlIcmpProtocol() fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); - // ICMPv6 ident and seq need to be handled as 'unknown' since Wireshark - // doesn't define display filters for the same + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); } PdmlDefaultProtocol* PdmlIcmpProtocol::createInstance() @@ -1370,6 +1404,7 @@ void PdmlIcmpProtocol::unknownFieldHandler(QString name, int pos, int size, { QString addrHexStr = attributes.value("value").toString(); + // Wireshark 1.4.x does not have these as filterable fields if (attributes.value("show").toString().startsWith("ID")) icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); else if (attributes.value("show").toString().startsWith("Sequence")) @@ -1386,6 +1421,87 @@ void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, stream->mutable_protocol()->RemoveLast(); } +// ---------------------------------------------------------- // +// PdmlIcmp6Protocol // +// ---------------------------------------------------------- // + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + pdmlProtoName_ = "icmpv6"; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlDefaultProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + // ---------------------------------------------------------- // // PdmlIgmpProtocol // @@ -1439,7 +1555,7 @@ void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, { version_ = attributes.value("show").toString().toUInt(&isOk); } - if (name == "igmp.type") + else if (name == "igmp.type") { uint type = valueHexStr.toUInt(&isOk, kBaseHex); if (type == kIgmpQuery) @@ -1453,7 +1569,7 @@ void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, } igmp->set_type(type); } - if (name == "igmp.record_type") + else if (name == "igmp.record_type") { OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( @@ -1504,11 +1620,129 @@ void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently if (version_ == 0) stream->mutable_protocol()->RemoveLast(); } +// ---------------------------------------------------------- // +// PdmlMldProtocol // +// ---------------------------------------------------------- // + +PdmlMldProtocol::PdmlMldProtocol() +{ + pdmlProtoName_ = "mld"; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlDefaultProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + + // ---------------------------------------------------------- // // PdmlTcpProtocol // // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdml_p.h index 5c557ed..2594b9b 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -250,6 +250,7 @@ public: class PdmlIcmpProtocol : public PdmlDefaultProtocol { + friend class PdmlIcmp6Protocol; public: PdmlIcmpProtocol(); @@ -270,6 +271,50 @@ private: static const uint kIcmp6EchoReply = 129; }; +class PdmlMldProtocol : public PdmlDefaultProtocol +{ + friend class PdmlIcmp6Protocol; +public: + PdmlMldProtocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +class PdmlIcmp6Protocol : public PdmlDefaultProtocol +{ +public: + PdmlIcmp6Protocol(); + + static PdmlDefaultProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlDefaultProtocol *proto_; +}; + class PdmlIgmpProtocol : public PdmlDefaultProtocol { public: @@ -292,7 +337,6 @@ private: static const uint kIgmpV3Query = 0xFE11; uint version_; - }; class PdmlTcpProtocol : public PdmlDefaultProtocol From 7532a1d576b1784716002f79ef72b24a01f81b03 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 8 Apr 2011 23:39:20 +0530 Subject: [PATCH 153/294] Fixed crash in drone when inter packet/burst gap > 1 sec. --- server/abstractport.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 63db109..cd16732 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -129,21 +129,21 @@ void AbstractPort::updatePacketList() if (streamList_[i]->isEnabled()) { long numPackets, numBursts; - long ibg, ipg; + long ibg = 0, ipg = 0; switch (streamList_[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: numBursts = streamList_[i]->numBursts(); numPackets = streamList_[i]->burstSize(); - ibg = 1000000/streamList_[i]->burstRate(); - ipg = 0; + if (streamList_[i]->burstRate() > 0) + ibg = 1000000/streamList_[i]->burstRate(); break; case OstProto::StreamControl::e_su_packets: numBursts = 1; numPackets = streamList_[i]->numPackets(); - ibg = 0; - ipg = 1000000/streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + ipg = 1000000/streamList_[i]->packetRate(); break; default: qWarning("Unhandled stream control unit %d", From 194aac9b7df02a3e9b47dea0a7e618419c647fc6 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 9 Apr 2011 17:13:49 +0530 Subject: [PATCH 154/294] Reorganized PDML code into multiple files --- common/ostproto.pro | 4 + common/pcapfileformat.cpp | 2 +- common/pdml_p.cpp | 666 ++------------------------------------ common/pdml_p.h | 166 +++------- common/pdmlfileformat.cpp | 2 +- common/pdmlprotocol.cpp | 158 +++++++++ common/pdmlprotocol.h | 67 ++++ common/pdmlreader.cpp | 506 +++++++++++++++++++++++++++++ common/pdmlreader.h | 75 +++++ 9 files changed, 870 insertions(+), 776 deletions(-) create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 3fe62f2..ee738e1 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -63,6 +63,8 @@ HEADERS += \ fileformat.h \ pcapfileformat.h \ pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlreader.h \ pdml_p.h \ protocolmanager.h \ protocollist.h \ @@ -106,6 +108,8 @@ SOURCES += \ fileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlreader.cpp \ pdml_p.cpp \ protocolmanager.cpp \ protocollist.cpp \ diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 106399f..9751c2c 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -19,7 +19,7 @@ along with this program. If not, see #include "pcapfileformat.h" -#include "pdml_p.h" +#include "pdmlreader.h" #include "ostprotolib.h" #include "streambase.h" #include "hexdump.pb.h" diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 42973e0..9164157 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2011 Srivats P. This file is part of "Ostinato" @@ -19,10 +19,7 @@ along with this program. If not, see #include "pdml_p.h" -#include "abstractprotocol.h" -#include "pcapfileformat.h" #include "protocolmanager.h" -#include "streambase.h" #include "arp.pb.h" #include "eth2.pb.h" @@ -43,9 +40,7 @@ along with this program. If not, see #include "udp.pb.h" #include "vlan.pb.h" -#include - -#include +//#include #include #include @@ -54,627 +49,7 @@ extern ProtocolManager *OstProtocolManager; const int kBaseHex = 16; -static PdmlReader *gPdmlReader = NULL; - -PdmlDefaultProtocol::PdmlDefaultProtocol() -{ - ostProtoId_ = -1; -} - -PdmlDefaultProtocol::~PdmlDefaultProtocol() -{ -} - -PdmlDefaultProtocol* PdmlDefaultProtocol::createInstance() -{ - return new PdmlDefaultProtocol(); -} - -QString PdmlDefaultProtocol::pdmlProtoName() const -{ - return pdmlProtoName_; -} - -int PdmlDefaultProtocol::ostProtoId() const -{ - return ostProtoId_; -} - -bool PdmlDefaultProtocol::hasField(QString name) const -{ - return fieldMap_.contains(name); -} - -int PdmlDefaultProtocol::fieldId(QString name) const -{ - return fieldMap_.value(name); -} - -void PdmlDefaultProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes& /*attributes*/, - int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, - OstProto::Stream* /*stream*/) -{ - return; // do nothing! -} - -void PdmlDefaultProtocol::prematureEndHandler(int /*pos*/, - OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) -{ - return; // do nothing! -} - -void PdmlDefaultProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, - OstProto::Stream* /*stream*/) -{ - return; // do nothing! -} - -void PdmlDefaultProtocol::fieldHandler(QString name, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - if (hasField(name)) - { - QString valueHexStr = attributes.value("value").toString(); - - qDebug("\t(KNOWN) fieldName:%s, value:%s", - name.toAscii().constData(), - valueHexStr.toAscii().constData()); - - knownFieldHandler(name, valueHexStr, pbProto); - } - else - { - int pos = -1; - int size = -1; - - if (!attributes.value("pos").isEmpty()) - pos = attributes.value("pos").toString().toInt(); - if (!attributes.value("size").isEmpty()) - size = attributes.value("size").toString().toInt(); - - qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", - name.toAscii().constData(), pos, size); - - unknownFieldHandler(name, pos, size, attributes, pbProto, stream); - } -} - -void PdmlDefaultProtocol::knownFieldHandler(QString name, QString valueHexStr, - OstProto::Protocol *pbProto) -{ - const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); - const google::protobuf::FieldDescriptor *extDesc = - protoRefl->FindKnownExtensionByNumber(ostProtoId()); - - google::protobuf::Message *msg = - protoRefl->MutableMessage(pbProto,extDesc); - - const google::protobuf::Reflection *msgRefl = msg->GetReflection(); - const google::protobuf::FieldDescriptor *fieldDesc = - msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); - - bool isOk; - - Q_ASSERT(fieldDesc != NULL); - switch(fieldDesc->cpp_type()) - { - case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: - msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO - case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: - msgRefl->SetUInt32(msg, fieldDesc, - valueHexStr.toUInt(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: - msgRefl->SetUInt64(msg, fieldDesc, - valueHexStr.toULongLong(&isOk, kBaseHex)); - break; - case google::protobuf::FieldDescriptor::CPPTYPE_STRING: - { - QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); - std::string str(hexVal.constData(), hexVal.size()); - msgRefl->SetString(msg, fieldDesc, str); - break; - } - default: - qDebug("%s: unhandled cpptype = %d", __FUNCTION__, - fieldDesc->cpp_type()); - } -} - -void PdmlDefaultProtocol::unknownFieldHandler(QString name, - int pos, int size, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - return; // do nothing! -} - - -// ---------------------------------------------------------- // -// PdmlReader // -// ---------------------------------------------------------- // -PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) -{ - gPdmlReader = this; - pcap_ = NULL; - streams_ = streams; - - currentStream_ = NULL; - prevStream_ = NULL; - - stop_ = NULL; - - factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); - factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); - factory_.insert("frame", PdmlFrameProtocol::createInstance); - - factory_.insert("arp", PdmlArpProtocol::createInstance); - factory_.insert("eth", PdmlEthProtocol::createInstance); - factory_.insert("http", PdmlTextProtocol::createInstance); - factory_.insert("icmp", PdmlIcmpProtocol::createInstance); - factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); - factory_.insert("igmp", PdmlIgmpProtocol::createInstance); - factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); - factory_.insert("imap", PdmlTextProtocol::createInstance); - factory_.insert("ip", PdmlIp4Protocol::createInstance); - factory_.insert("ipv6", PdmlIp6Protocol::createInstance); - factory_.insert("llc", PdmlLlcProtocol::createInstance); - factory_.insert("nntp", PdmlTextProtocol::createInstance); - factory_.insert("pop", PdmlTextProtocol::createInstance); - factory_.insert("rtsp", PdmlTextProtocol::createInstance); - factory_.insert("sdp", PdmlTextProtocol::createInstance); - factory_.insert("sip", PdmlTextProtocol::createInstance); - factory_.insert("smtp", PdmlTextProtocol::createInstance); - factory_.insert("tcp", PdmlTcpProtocol::createInstance); - factory_.insert("udp", PdmlUdpProtocol::createInstance); - factory_.insert("udplite", PdmlUdpProtocol::createInstance); - factory_.insert("vlan", PdmlVlanProtocol::createInstance); -} - -PdmlReader::~PdmlReader() -{ -} - -bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) -{ - setDevice(device); - pcap_ = pcap; - stop_ = stop; - - while (!atEnd()) - { - readNext(); - if (isStartElement()) - { - if (name() == "pdml") - readPdml(); - else - raiseError("Not a pdml file!"); - } - } - - if (error() && (errorString() != "USER-CANCEL")) - { - qDebug("Line %lld", lineNumber()); - qDebug("Col %lld", columnNumber()); - qDebug("%s", errorString().toAscii().constData()); - return false; - } - return true; -} - -// TODO: use a temp pool to avoid a lot of new/delete -PdmlDefaultProtocol* PdmlReader::allocPdmlProtocol(QString protoName) -{ - // If protoName is not known, we use a hexdump - if (!factory_.contains(protoName)) - protoName = "hexdump"; - - return (*(factory_.value(protoName)))(); -} - -void PdmlReader::freePdmlProtocol(PdmlDefaultProtocol *proto) -{ - delete proto; -} - -bool PdmlReader::isDontCareProto() -{ - Q_ASSERT(isStartElement() && name() == "proto"); - - QStringRef protoName = attributes().value("name"); - - if (protoName.isEmpty() || (protoName == "expert")) - return true; - - return false; -} - -void PdmlReader::skipElement() -{ - Q_ASSERT(isStartElement()); - - qDebug("skipping element - <%s>", - name().toString().toAscii().constData()); - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - skipElement(); - } -} - -void PdmlReader::readPdml() -{ - Q_ASSERT(isStartElement() && name() == "pdml"); - - packetCount_ = 1; - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "packet") - readPacket(); - else - skipElement(); - } - } -} - -void PdmlReader::readPacket() -{ - PcapFileFormat::PcapPacketHeader pktHdr; - - Q_ASSERT(isStartElement() && name() == "packet"); - - qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); - - skipUntilEnd_ = false; - - // XXX: we play dumb and convert each packet to a stream, for now - prevStream_ = currentStream_; - currentStream_ = streams_->add_stream(); - currentStream_->mutable_stream_id()->set_id(packetCount_); - currentStream_->mutable_core()->set_is_enabled(true); - - // Set to a high number; will get reset to correct value during parse - currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! - - expPos_ = 0; - - if (pcap_) - pcap_->readPacket(pktHdr, pktBuf_); - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (skipUntilEnd_) - skipElement(); - else if (name() == "proto") - readProto(); - else if (name() == "field") - readField(NULL, NULL); // TODO: top level field!!!! - else - skipElement(); - } - } - - currentStream_->mutable_core()->set_name(""); // FIXME - - // If trailing bytes are missing, add those from the pcap - if ((expPos_ < pktBuf_.size()) && pcap_) - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - OstProto::HexDump *hexDump = proto->MutableExtension( - OstProto::hexDump); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - qDebug("adding trailing %d bytes starting from %d", - pktBuf_.size() - expPos_, expPos_); - hexDump->set_content(pktBuf_.constData() + expPos_, - pktBuf_.size() - expPos_); - hexDump->set_pad_until_end(false); - } - - packetCount_++; - emit progress(int(characterOffset()*100/device()->size())); // in % - if (prevStream_) - prevStream_->mutable_control()->CopyFrom(currentStream_->control()); - if (stop_ && *stop_) - raiseError("USER-CANCEL"); -} - -void PdmlReader::readProto() -{ - PdmlDefaultProtocol *pdmlProto = NULL; - OstProto::Protocol *pbProto = NULL; - - Q_ASSERT(isStartElement() && name() == "proto"); - - QString protoName; - int pos = -1; - int size = -1; - - if (!attributes().value("name").isEmpty()) - protoName = attributes().value("name").toString(); - if (!attributes().value("pos").isEmpty()) - pos = attributes().value("pos").toString().toInt(); - if (!attributes().value("size").isEmpty()) - size = attributes().value("size").toString().toInt(); - - qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", - protoName.toAscii().constData(), pos, expPos_, size); - - // This is a heuristic to skip protocols which are not part of - // this frame, but of a reassembled segment spanning several frames - // 1. Proto starting pos is 0, but we've already seen some protocols - // 2. Protocol Size exceeds frame length - if (((pos == 0) && (currentStream_->protocol_size() > 0)) - || ((pos + size) > int(currentStream_->core().frame_len()))) - { - skipElement(); - return; - } - - if (isDontCareProto()) - { - skipElement(); - return; - } - - // if we detect a gap between subsequent protocols, we "fill-in" - // with a "hexdump" from the pcap - if (pos > expPos_ && pcap_) - { - appendHexDumpProto(expPos_, pos - expPos_); - expPos_ = pos; - } - - // for unknown protocol, read a hexdump from the pcap - if (!factory_.contains(protoName) && pcap_) - { - int size = -1; - - if (!attributes().value("size").isEmpty()) - size = attributes().value("size").toString().toInt(); - - // Check if this proto is a subset of previous proto - if so, do nothing - if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) - { - qDebug("subset proto"); - skipElement(); - return; - } - - if (pos >= 0 && size > 0 - && ((pos + size) <= pktBuf_.size())) - { - appendHexDumpProto(pos, size); - expPos_ += size; - - skipElement(); - return; - } - } - - pdmlProto = appendPdmlProto(protoName, &pbProto); - - qDebug("%s: preProtocolHandler(expPos = %d)", - protoName.toAscii().constData(), expPos_); - pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, - currentStream_); - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "proto") - { - // an embedded proto - qDebug("embedded proto: %s\n", attributes().value("name") - .toString().toAscii().constData()); - - if (isDontCareProto()) - { - skipElement(); - continue; - } - - // if we are in the midst of processing a protocol, we - // end it prematurely before we start processing the - // embedded protocol - // - // XXX: pdmlProto may be NULL for a sequence of embedded protos - if (pdmlProto) - { - int endPos = -1; - - if (!attributes().value("pos").isEmpty()) - endPos = attributes().value("pos").toString().toInt(); - - pdmlProto->prematureEndHandler(endPos, pbProto, - currentStream_); - pdmlProto->postProtocolHandler(pbProto, currentStream_); - - StreamBase s; - s.protoDataCopyFrom(*currentStream_); - expPos_ = s.frameProtocolLength(0); - } - - readProto(); - - pdmlProto = NULL; - pbProto = NULL; - } - else if (name() == "field") - { - if ((protoName == "fake-field-wrapper") && - (attributes().value("name") == "tcp.segments")) - { - skipElement(); - qDebug("[skipping reassembled tcp segments]"); - - skipUntilEnd_ = true; - continue; - } - - if (pdmlProto == NULL) - { - pdmlProto = appendPdmlProto(protoName, &pbProto); - - qDebug("%s: preProtocolHandler(expPos = %d)", - protoName.toAscii().constData(), expPos_); - pdmlProto->preProtocolHandler(protoName, attributes(), - expPos_, pbProto, currentStream_); - } - - readField(pdmlProto, pbProto); - } - else - skipElement(); - } - } - - // Close-off current protocol - if (pdmlProto) - { - pdmlProto->postProtocolHandler(pbProto, currentStream_); - freePdmlProtocol(pdmlProto); - - StreamBase s; - s.protoDataCopyFrom(*currentStream_); - expPos_ = s.frameProtocolLength(0); - } -} - -void PdmlReader::readField(PdmlDefaultProtocol *pdmlProto, - OstProto::Protocol *pbProto) -{ - Q_ASSERT(isStartElement() && name() == "field"); - - // fields with "hide='yes'" are informational and should be skipped - if (attributes().value("hide") == "yes") - { - skipElement(); - return; - } - - QString fieldName = attributes().value("name").toString(); - - qDebug(" fieldName:%s", fieldName.toAscii().constData()); - - pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); - - while (!atEnd()) - { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - { - if (name() == "proto") - { - // Since we are in the midst of processing a protocol, we - // end it prematurely before we start processing the - // embedded protocol - // - int endPos = -1; - - if (!attributes().value("pos").isEmpty()) - endPos = attributes().value("pos").toString().toInt(); - - pdmlProto->prematureEndHandler(endPos, pbProto, - currentStream_); - pdmlProto->postProtocolHandler(pbProto, currentStream_); - - StreamBase s; - s.protoDataCopyFrom(*currentStream_); - expPos_ = s.frameProtocolLength(0); - - readProto(); - } - else if (name() == "field") - readField(pdmlProto, pbProto); - else - skipElement(); - } - } -} - -void PdmlReader::appendHexDumpProto(int offset, int size) -{ - OstProto::Protocol *proto = currentStream_->add_protocol(); - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - qDebug("filling in gap of %d bytes starting from %d", size, offset); - hexDump->set_content(pktBuf_.constData() + offset, size); - hexDump->set_pad_until_end(false); -} - -PdmlDefaultProtocol* PdmlReader::appendPdmlProto(const QString &protoName, - OstProto::Protocol **pbProto) -{ - PdmlDefaultProtocol* pdmlProto = allocPdmlProtocol(protoName); - Q_ASSERT(pdmlProto != NULL); - - int protoId = pdmlProto->ostProtoId(); - - if (protoId > 0) // Non-Base Class - { - OstProto::Protocol *proto = currentStream_->add_protocol(); - - proto->mutable_protocol_id()->set_id(protoId); - - const google::protobuf::Reflection *msgRefl = proto->GetReflection(); - const google::protobuf::FieldDescriptor *fieldDesc = - msgRefl->FindKnownExtensionByNumber(protoId); - - // TODO: if !fDesc - // init default values of all fields in protocol - msgRefl->MutableMessage(proto, fieldDesc); - - *pbProto = proto; - - qDebug("%s: name = %s", __FUNCTION__, - protoName.toAscii().constData()); - } - else - *pbProto = NULL; - - return pdmlProto; -} - +//static PdmlReader *gPdmlReader = NULL; // ---------------------------------------------------------- // // PdmlUnknownProtocol // @@ -688,7 +63,7 @@ PdmlUnknownProtocol::PdmlUnknownProtocol() endPos_ = expPos_ = -1; } -PdmlDefaultProtocol* PdmlUnknownProtocol::createInstance() +PdmlProtocol* PdmlUnknownProtocol::createInstance() { return new PdmlUnknownProtocol(); } @@ -795,7 +170,7 @@ PdmlGenInfoProtocol::PdmlGenInfoProtocol() pdmlProtoName_ = "geninfo"; } -PdmlDefaultProtocol* PdmlGenInfoProtocol::createInstance() +PdmlProtocol* PdmlGenInfoProtocol::createInstance() { return new PdmlGenInfoProtocol(); } @@ -818,7 +193,7 @@ PdmlFrameProtocol::PdmlFrameProtocol() pdmlProtoName_ = "frame"; } -PdmlDefaultProtocol* PdmlFrameProtocol::createInstance() +PdmlProtocol* PdmlFrameProtocol::createInstance() { return new PdmlFrameProtocol(); } @@ -872,7 +247,7 @@ PdmlSvlanProtocol::PdmlSvlanProtocol() ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; } -PdmlDefaultProtocol* PdmlSvlanProtocol::createInstance() +PdmlProtocol* PdmlSvlanProtocol::createInstance() { return new PdmlSvlanProtocol(); } @@ -964,7 +339,7 @@ PdmlVlanProtocol::PdmlVlanProtocol() ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; } -PdmlDefaultProtocol* PdmlVlanProtocol::createInstance() +PdmlProtocol* PdmlVlanProtocol::createInstance() { return new PdmlVlanProtocol(); } @@ -1040,7 +415,7 @@ PdmlEthProtocol::PdmlEthProtocol() fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); } -PdmlDefaultProtocol* PdmlEthProtocol::createInstance() +PdmlProtocol* PdmlEthProtocol::createInstance() { return new PdmlEthProtocol(); } @@ -1143,7 +518,7 @@ PdmlLlcProtocol::PdmlLlcProtocol() fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); } -PdmlDefaultProtocol* PdmlLlcProtocol::createInstance() +PdmlProtocol* PdmlLlcProtocol::createInstance() { return new PdmlLlcProtocol(); } @@ -1207,7 +582,7 @@ PdmlArpProtocol::PdmlArpProtocol() OstProto::Arp::kTargetProtoAddrFieldNumber); } -PdmlDefaultProtocol* PdmlArpProtocol::createInstance() +PdmlProtocol* PdmlArpProtocol::createInstance() { return new PdmlArpProtocol(); } @@ -1235,7 +610,7 @@ PdmlIp4Protocol::PdmlIp4Protocol() fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); } -PdmlDefaultProtocol* PdmlIp4Protocol::createInstance() +PdmlProtocol* PdmlIp4Protocol::createInstance() { return new PdmlIp4Protocol(); } @@ -1306,7 +681,7 @@ PdmlIp6Protocol::PdmlIp6Protocol() // ipv6.src and ipv6.dst handled as unknown fields } -PdmlDefaultProtocol* PdmlIp6Protocol::createInstance() +PdmlProtocol* PdmlIp6Protocol::createInstance() { return new PdmlIp6Protocol(); } @@ -1370,7 +745,7 @@ PdmlIcmpProtocol::PdmlIcmpProtocol() OstProto::Icmp::kSequenceFieldNumber); } -PdmlDefaultProtocol* PdmlIcmpProtocol::createInstance() +PdmlProtocol* PdmlIcmpProtocol::createInstance() { return new PdmlIcmpProtocol(); } @@ -1433,7 +808,7 @@ PdmlIcmp6Protocol::PdmlIcmp6Protocol() proto_ = NULL; } -PdmlDefaultProtocol* PdmlIcmp6Protocol::createInstance() +PdmlProtocol* PdmlIcmp6Protocol::createInstance() { return new PdmlIcmp6Protocol(); } @@ -1524,7 +899,7 @@ PdmlIgmpProtocol::PdmlIgmpProtocol() OstProto::Gmp::kGroupRecordCountFieldNumber); } -PdmlDefaultProtocol* PdmlIgmpProtocol::createInstance() +PdmlProtocol* PdmlIgmpProtocol::createInstance() { return new PdmlIgmpProtocol(); } @@ -1651,7 +1026,7 @@ PdmlMldProtocol::PdmlMldProtocol() OstProto::Gmp::kGroupRecordCountFieldNumber); } -PdmlDefaultProtocol* PdmlMldProtocol::createInstance() +PdmlProtocol* PdmlMldProtocol::createInstance() { return new PdmlMldProtocol(); } @@ -1763,7 +1138,7 @@ PdmlTcpProtocol::PdmlTcpProtocol() fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); } -PdmlDefaultProtocol* PdmlTcpProtocol::createInstance() +PdmlProtocol* PdmlTcpProtocol::createInstance() { return new PdmlTcpProtocol(); } @@ -1854,7 +1229,7 @@ PdmlUdpProtocol::PdmlUdpProtocol() fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); } -PdmlDefaultProtocol* PdmlUdpProtocol::createInstance() +PdmlProtocol* PdmlUdpProtocol::createInstance() { return new PdmlUdpProtocol(); } @@ -1883,7 +1258,7 @@ PdmlTextProtocol::PdmlTextProtocol() ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; } -PdmlDefaultProtocol* PdmlTextProtocol::createInstance() +PdmlProtocol* PdmlTextProtocol::createInstance() { return new PdmlTextProtocol(); } @@ -2026,4 +1401,3 @@ void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, detectEol_ = true; contentType_ = kUnknownContent; } - diff --git a/common/pdml_p.h b/common/pdml_p.h index 2594b9b..cfcd025 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2011 Srivats P. This file is part of "Ostinato" @@ -16,108 +16,18 @@ 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 */ + #ifndef _PDML_P_H #define _PDML_P_H -#include "protocol.pb.h" +#include "pdmlprotocol.h" -#include -#include -#include -#include -#include - -// TODO: add const where possible - -class PdmlDefaultProtocol -{ -public: - PdmlDefaultProtocol(); // TODO: make private - virtual ~PdmlDefaultProtocol(); - - static PdmlDefaultProtocol* createInstance(); - - QString pdmlProtoName() const; - int ostProtoId() const; - bool hasField(QString name) const; - int fieldId(QString name) const; - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, - OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); - - void fieldHandler(QString name, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - void knownFieldHandler(QString name, QString valueHexStr, - OstProto::Protocol *pbProto); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - -protected: - QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ - int ostProtoId_; - QMap fieldMap_; -}; - -class PdmlUnknownProtocol; -class PcapFileFormat; -class PdmlReader : public QObject, public QXmlStreamReader -{ - Q_OBJECT - //friend class PdmlUnknownProtocol; -public: - PdmlReader(OstProto::StreamConfigList *streams); - ~PdmlReader(); - - bool read(QIODevice *device, PcapFileFormat *pcap = NULL, - bool *stop = NULL); -signals: - void progress(int value); - -private: - PdmlDefaultProtocol* allocPdmlProtocol(QString protoName); - void freePdmlProtocol(PdmlDefaultProtocol *proto); - - bool isDontCareProto(); - void skipElement(); - - void readPdml(); - void readPacket(); - void readProto(); - void readField(PdmlDefaultProtocol *pdmlProto, - OstProto::Protocol *pbProto); - - void appendHexDumpProto(int offset, int size); - PdmlDefaultProtocol* appendPdmlProto(const QString &protoName, - OstProto::Protocol **pbProto); - - typedef PdmlDefaultProtocol* (*FactoryMethod)(); - - QMap factory_; - - bool *stop_; - OstProto::StreamConfigList *streams_; - PcapFileFormat *pcap_; - QByteArray pktBuf_; - - int packetCount_; - int expPos_; - bool skipUntilEnd_; - OstProto::Stream *prevStream_; - OstProto::Stream *currentStream_; -}; - -class PdmlUnknownProtocol : public PdmlDefaultProtocol +class PdmlUnknownProtocol : public PdmlProtocol { public: PdmlUnknownProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -134,44 +44,44 @@ private: int expPos_; }; -class PdmlGenInfoProtocol : public PdmlDefaultProtocol +class PdmlGenInfoProtocol : public PdmlProtocol { public: PdmlGenInfoProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); }; -class PdmlFrameProtocol : public PdmlDefaultProtocol +class PdmlFrameProtocol : public PdmlProtocol { public: PdmlFrameProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); }; -class PdmlEthProtocol : public PdmlDefaultProtocol +class PdmlEthProtocol : public PdmlProtocol { public: PdmlEthProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); }; -class PdmlSvlanProtocol : public PdmlDefaultProtocol +class PdmlSvlanProtocol : public PdmlProtocol { public: PdmlSvlanProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -181,12 +91,12 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); }; -class PdmlVlanProtocol : public PdmlDefaultProtocol +class PdmlVlanProtocol : public PdmlProtocol { public: PdmlVlanProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -196,12 +106,12 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); }; -class PdmlLlcProtocol : public PdmlDefaultProtocol +class PdmlLlcProtocol : public PdmlProtocol { public: PdmlLlcProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, @@ -210,20 +120,20 @@ public: OstProto::Stream *stream); }; -class PdmlArpProtocol : public PdmlDefaultProtocol +class PdmlArpProtocol : public PdmlProtocol { public: PdmlArpProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); }; -class PdmlIp4Protocol : public PdmlDefaultProtocol +class PdmlIp4Protocol : public PdmlProtocol { public: PdmlIp4Protocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, @@ -234,12 +144,12 @@ private: QByteArray options_; }; -class PdmlIp6Protocol : public PdmlDefaultProtocol +class PdmlIp6Protocol : public PdmlProtocol { public: PdmlIp6Protocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, @@ -248,13 +158,13 @@ public: OstProto::Stream *stream); }; -class PdmlIcmpProtocol : public PdmlDefaultProtocol +class PdmlIcmpProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: PdmlIcmpProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -271,13 +181,13 @@ private: static const uint kIcmp6EchoReply = 129; }; -class PdmlMldProtocol : public PdmlDefaultProtocol +class PdmlMldProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: PdmlMldProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -293,12 +203,12 @@ private: uint protoSize_; }; -class PdmlIcmp6Protocol : public PdmlDefaultProtocol +class PdmlIcmp6Protocol : public PdmlProtocol { public: PdmlIcmp6Protocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -312,15 +222,15 @@ public: private: PdmlIcmpProtocol icmp_; PdmlMldProtocol mld_; - PdmlDefaultProtocol *proto_; + PdmlProtocol *proto_; }; -class PdmlIgmpProtocol : public PdmlDefaultProtocol +class PdmlIgmpProtocol : public PdmlProtocol { public: PdmlIgmpProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, @@ -339,12 +249,12 @@ private: uint version_; }; -class PdmlTcpProtocol : public PdmlDefaultProtocol +class PdmlTcpProtocol : public PdmlProtocol { public: PdmlTcpProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, @@ -356,22 +266,22 @@ private: QByteArray segmentData_; }; -class PdmlUdpProtocol : public PdmlDefaultProtocol +class PdmlUdpProtocol : public PdmlProtocol { public: PdmlUdpProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); }; -class PdmlTextProtocol : public PdmlDefaultProtocol +class PdmlTextProtocol : public PdmlProtocol { public: PdmlTextProtocol(); - static PdmlDefaultProtocol* createInstance(); + static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 0f85542..73b70e3 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -20,7 +20,7 @@ along with this program. If not, see #include "pdmlfileformat.h" #include "ostprotolib.h" -#include "pdml_p.h" +#include "pdmlreader.h" #include #include diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..89cdabc --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,158 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +const int kBaseHex = 16; + +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +PdmlProtocol::~PdmlProtocol() +{ +} + +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +QString PdmlProtocol::pdmlProtoName() const +{ + return pdmlProtoName_; +} + +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +void PdmlProtocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..0e99b9f --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +// TODO: add const where possible + +class PdmlProtocol +{ +public: + PdmlProtocol(); // TODO: make private + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + QString pdmlProtoName() const; + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ + int ostProtoId_; + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..0e8af62 --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,506 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdml_p.h" // TODO: Remove + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + Q_ASSERT(isStartElement() && name() == "pdml"); + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..a4b6299 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT + //friend class PdmlUnknownProtocol; +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif From 9771c50592c0ea9079e3822debad052e418f2f78 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 9 Apr 2011 20:22:35 +0530 Subject: [PATCH 155/294] Fixed gcc4 errors and warnings --- client/port.cpp | 2 +- common/pcapfileformat.cpp | 2 +- common/pdml_p.cpp | 109 +++++++++++++++++++------------------- common/pdmlfileformat.cpp | 15 +++++- common/pdmlprotocol.cpp | 6 +-- test/test.pro | 23 +++++++- 6 files changed, 94 insertions(+), 63 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 8068c41..1acb94d 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -238,7 +238,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error) if (fmt == NULL) goto _fail; - if (optDialog = fmt->openOptionsDialog()) + if ((optDialog = fmt->openOptionsDialog())) { int ret; optDialog->setParent(mainWindow, Qt::Dialog); diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index 9751c2c..bc0ccd6 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -646,7 +646,7 @@ QDialog* PcapFileFormat::openOptionsDialog() return importDialog_; } -bool PcapFileFormat::isMyFileFormat(const QString fileName) +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) { // TODO return true; diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 9164157..322d714 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -24,6 +24,7 @@ along with this program. If not, see #include "arp.pb.h" #include "eth2.pb.h" #include "dot3.pb.h" +#include "gmp.pb.h" #include "hexdump.pb.h" #include "llc.pb.h" #include "mac.pb.h" @@ -68,9 +69,9 @@ PdmlProtocol* PdmlUnknownProtocol::createInstance() return new PdmlUnknownProtocol(); } -void PdmlUnknownProtocol::preProtocolHandler(QString name, +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream) + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { bool isOk; int size; @@ -130,9 +131,9 @@ void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, endPos_ = expPos_ = -1; } -void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); @@ -198,9 +199,9 @@ PdmlProtocol* PdmlFrameProtocol::createInstance() return new PdmlFrameProtocol(); } -void PdmlFrameProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "frame.len") { @@ -252,8 +253,8 @@ PdmlProtocol* PdmlSvlanProtocol::createInstance() return new PdmlSvlanProtocol(); } -void PdmlSvlanProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); @@ -279,8 +280,8 @@ void PdmlSvlanProtocol::preProtocolHandler(QString name, } } -void PdmlSvlanProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) @@ -344,8 +345,8 @@ PdmlProtocol* PdmlVlanProtocol::createInstance() return new PdmlVlanProtocol(); } -void PdmlVlanProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); @@ -371,8 +372,8 @@ void PdmlVlanProtocol::preProtocolHandler(QString name, } } -void PdmlVlanProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (name == "vlan.id") @@ -420,9 +421,9 @@ PdmlProtocol* PdmlEthProtocol::createInstance() return new PdmlEthProtocol(); } -void PdmlEthProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "eth.vlan.tpid") { @@ -523,9 +524,9 @@ PdmlProtocol* PdmlLlcProtocol::createInstance() return new PdmlLlcProtocol(); } -void PdmlLlcProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "llc.oui") { @@ -554,7 +555,7 @@ void PdmlLlcProtocol::unknownFieldHandler(QString name, int pos, int size, } void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) + OstProto::Stream* /*stream*/) { OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); @@ -615,9 +616,9 @@ PdmlProtocol* PdmlIp4Protocol::createInstance() return new PdmlIp4Protocol(); } -void PdmlIp4Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; @@ -686,9 +687,9 @@ PdmlProtocol* PdmlIp6Protocol::createInstance() return new PdmlIp6Protocol(); } -void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; @@ -711,7 +712,7 @@ void PdmlIp6Protocol::unknownFieldHandler(QString name, int pos, int size, } void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) + OstProto::Stream* /*stream*/) { OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); @@ -751,8 +752,8 @@ PdmlProtocol* PdmlIcmpProtocol::createInstance() } void PdmlIcmpProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream) + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); @@ -766,9 +767,9 @@ void PdmlIcmpProtocol::preProtocolHandler(QString name, icmp->set_type(kIcmpInvalidType); } -void PdmlIcmpProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); @@ -904,9 +905,9 @@ PdmlProtocol* PdmlIgmpProtocol::createInstance() return new PdmlIgmpProtocol(); } -void PdmlIgmpProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); @@ -918,9 +919,9 @@ void PdmlIgmpProtocol::preProtocolHandler(QString name, version_ = 0; } -void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); @@ -992,7 +993,7 @@ void PdmlIgmpProtocol::unknownFieldHandler(QString name, int pos, int size, } } -void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { // version is 0 for IGMP like protocols such as RGMP which we don't @@ -1031,9 +1032,9 @@ PdmlProtocol* PdmlMldProtocol::createInstance() return new PdmlMldProtocol(); } -void PdmlMldProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); @@ -1046,9 +1047,9 @@ void PdmlMldProtocol::preProtocolHandler(QString name, protoSize_ = attributes.value("size").toString().toUInt(&isOk); } -void PdmlMldProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); @@ -1143,9 +1144,9 @@ PdmlProtocol* PdmlTcpProtocol::createInstance() return new PdmlTcpProtocol(); } -void PdmlTcpProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (name == "tcp.options") options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); @@ -1235,7 +1236,7 @@ PdmlProtocol* PdmlUdpProtocol::createInstance() } void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) + OstProto::Stream* /*stream*/) { OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); @@ -1263,7 +1264,7 @@ PdmlProtocol* PdmlTextProtocol::createInstance() return new PdmlTextProtocol(); } -void PdmlTextProtocol::preProtocolHandler(QString name, +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream) { @@ -1305,7 +1306,7 @@ _skip_pos_size_proc: void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream *stream) + OstProto::Stream* /*stream*/) { _retry: switch(contentType_) diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 73b70e3..9ba1f2d 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -49,15 +49,26 @@ bool PdmlFileFormat::openStreams(const QString fileName, emit status("Reading PDML packets..."); emit target(100); // in percentage - // TODO: fill in error string + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; - isOk = reader->read(&file); + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } goto _exit; _open_fail: isOk = false; +_user_cancel: _exit: delete reader; diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp index 89cdabc..5706798 100644 --- a/common/pdmlprotocol.cpp +++ b/common/pdmlprotocol.cpp @@ -150,9 +150,9 @@ void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, } } -void PdmlProtocol::unknownFieldHandler(QString name, - int pos, int size, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } diff --git a/test/test.pro b/test/test.pro index 8e055de..828df55 100644 --- a/test/test.pro +++ b/test/test.pro @@ -2,10 +2,29 @@ TEMPLATE = app CONFIG += qt console QT += xml network script INCLUDEPATH += "../rpc/" "../common/" -LIBS += -L"../common/debug" -lostproto +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 -POST_TARGETDEPS += "../common/debug/libostproto.a" HEADERS += SOURCES += main.cpp From e6b2274c9c96e0a4e5628074acd90a7074883328 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 10 Apr 2011 14:41:05 +0530 Subject: [PATCH 156/294] Implemented detection of MLD PDML support --- common/pdmlreader.cpp | 27 +++++++++++++++++++++++++++ common/pdmlreader.h | 1 + 2 files changed, 28 insertions(+) diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp index 0e8af62..f2ac300 100644 --- a/common/pdmlreader.cpp +++ b/common/pdmlreader.cpp @@ -103,6 +103,13 @@ PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) if (!factory_.contains(protoName)) protoName = "hexdump"; + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + return (*(factory_.value(protoName)))(); } @@ -143,8 +150,28 @@ void PdmlReader::skipElement() void PdmlReader::readPdml() { + QStringList creator; + Q_ASSERT(isStartElement() && name() == "pdml"); + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + packetCount_ = 1; while (!atEnd()) diff --git a/common/pdmlreader.h b/common/pdmlreader.h index a4b6299..29aa1ac 100644 --- a/common/pdmlreader.h +++ b/common/pdmlreader.h @@ -65,6 +65,7 @@ private: PcapFileFormat *pcap_; QByteArray pktBuf_; + bool isMldSupport_; int packetCount_; int expPos_; bool skipUntilEnd_; From 5db314722dfd74518b9a6916726382d25c63a3bb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 10 Apr 2011 21:03:47 +0530 Subject: [PATCH 157/294] PCAP/PDML import: class PdmlProtocol - removed protoName member; made constructor protected --- common/pdml_p.cpp | 34 ------------------- common/pdml_p.h | 72 ++++++++++++++++++++++------------------- common/pdmlprotocol.cpp | 5 --- common/pdmlprotocol.h | 7 ++-- 4 files changed, 41 insertions(+), 77 deletions(-) diff --git a/common/pdml_p.cpp b/common/pdml_p.cpp index 322d714..a36a056 100644 --- a/common/pdml_p.cpp +++ b/common/pdml_p.cpp @@ -19,8 +19,6 @@ along with this program. If not, see #include "pdml_p.h" -#include "protocolmanager.h" - #include "arp.pb.h" #include "eth2.pb.h" #include "dot3.pb.h" @@ -41,24 +39,17 @@ along with this program. If not, see #include "udp.pb.h" #include "vlan.pb.h" -//#include #include - #include -extern ProtocolManager *OstProtocolManager; - const int kBaseHex = 16; -//static PdmlReader *gPdmlReader = NULL; - // ---------------------------------------------------------- // // PdmlUnknownProtocol // // ---------------------------------------------------------- // PdmlUnknownProtocol::PdmlUnknownProtocol() { - pdmlProtoName_ = ""; ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; endPos_ = expPos_ = -1; @@ -168,7 +159,6 @@ void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, PdmlGenInfoProtocol::PdmlGenInfoProtocol() { - pdmlProtoName_ = "geninfo"; } PdmlProtocol* PdmlGenInfoProtocol::createInstance() @@ -176,22 +166,12 @@ PdmlProtocol* PdmlGenInfoProtocol::createInstance() return new PdmlGenInfoProtocol(); } -#if 0 // done in frame proto -void PdmlGenInfoProtocol::unknownFieldHandler(QString name, int pos, - int size, const QXmlStreamAttributes &attributes, OstProto::Stream *stream) -{ - if (name == "len") - stream->mutable_core()->set_frame_len(size+4); // TODO:check FCS -} -#endif - // ---------------------------------------------------------- // // PdmlFrameProtocol // // ---------------------------------------------------------- // PdmlFrameProtocol::PdmlFrameProtocol() { - pdmlProtoName_ = "frame"; } PdmlProtocol* PdmlFrameProtocol::createInstance() @@ -244,7 +224,6 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, PdmlSvlanProtocol::PdmlSvlanProtocol() { - pdmlProtoName_ = "ieee8021ad"; ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; } @@ -336,7 +315,6 @@ void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, PdmlVlanProtocol::PdmlVlanProtocol() { - pdmlProtoName_ = "vlan"; ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; } @@ -409,7 +387,6 @@ void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, PdmlEthProtocol::PdmlEthProtocol() { - pdmlProtoName_ = "eth"; ostProtoId_ = OstProto::Protocol::kMacFieldNumber; fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); @@ -511,7 +488,6 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, PdmlLlcProtocol::PdmlLlcProtocol() { - pdmlProtoName_ = "llc"; ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); @@ -571,7 +547,6 @@ void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlArpProtocol::PdmlArpProtocol() { - pdmlProtoName_ = "arp"; ostProtoId_ = OstProto::Protocol::kArpFieldNumber; fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); @@ -595,7 +570,6 @@ PdmlProtocol* PdmlArpProtocol::createInstance() PdmlIp4Protocol::PdmlIp4Protocol() { - pdmlProtoName_ = "ip"; ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); @@ -669,7 +643,6 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlIp6Protocol::PdmlIp6Protocol() { - pdmlProtoName_ = "ipv6"; ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); @@ -728,7 +701,6 @@ void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlIcmpProtocol::PdmlIcmpProtocol() { - pdmlProtoName_ = "icmp"; ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); @@ -803,7 +775,6 @@ void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlIcmp6Protocol::PdmlIcmp6Protocol() { - pdmlProtoName_ = "icmpv6"; ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; proto_ = NULL; @@ -885,7 +856,6 @@ void PdmlIcmp6Protocol::unknownFieldHandler(QString name, PdmlIgmpProtocol::PdmlIgmpProtocol() { - pdmlProtoName_ = "igmp"; ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; fieldMap_.insert("igmp.max_resp", @@ -1009,7 +979,6 @@ void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, PdmlMldProtocol::PdmlMldProtocol() { - pdmlProtoName_ = "mld"; ostProtoId_ = OstProto::Protocol::kMldFieldNumber; fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); @@ -1125,7 +1094,6 @@ void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, PdmlTcpProtocol::PdmlTcpProtocol() { - pdmlProtoName_ = "tcp"; ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); @@ -1219,7 +1187,6 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlUdpProtocol::PdmlUdpProtocol() { - pdmlProtoName_ = "udp"; // OR udplite ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); @@ -1255,7 +1222,6 @@ void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, PdmlTextProtocol::PdmlTextProtocol() { - pdmlProtoName_ = "text"; ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; } diff --git a/common/pdml_p.h b/common/pdml_p.h index cfcd025..1824d9d 100644 --- a/common/pdml_p.h +++ b/common/pdml_p.h @@ -25,8 +25,6 @@ along with this program. If not, see class PdmlUnknownProtocol : public PdmlProtocol { public: - PdmlUnknownProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -39,6 +37,9 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + private: int endPos_; int expPos_; @@ -47,40 +48,42 @@ private: class PdmlGenInfoProtocol : public PdmlProtocol { public: + static PdmlProtocol* createInstance(); + +protected: PdmlGenInfoProtocol(); - static PdmlProtocol* createInstance(); }; class PdmlFrameProtocol : public PdmlProtocol { public: - PdmlFrameProtocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); }; class PdmlEthProtocol : public PdmlProtocol { public: - PdmlEthProtocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); }; class PdmlSvlanProtocol : public PdmlProtocol { public: - PdmlSvlanProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -89,13 +92,13 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); }; class PdmlVlanProtocol : public PdmlProtocol { public: - PdmlVlanProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -104,13 +107,13 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); }; class PdmlLlcProtocol : public PdmlProtocol { public: - PdmlLlcProtocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -118,21 +121,22 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); }; class PdmlArpProtocol : public PdmlProtocol { public: - PdmlArpProtocol(); - static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); }; class PdmlIp4Protocol : public PdmlProtocol { public: - PdmlIp4Protocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -140,6 +144,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); private: QByteArray options_; }; @@ -147,8 +153,6 @@ private: class PdmlIp6Protocol : public PdmlProtocol { public: - PdmlIp6Protocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -156,14 +160,14 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); }; class PdmlIcmpProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: - PdmlIcmpProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -174,6 +178,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); private: static const uint kIcmpInvalidType = 0xFFFFFFFF; @@ -185,8 +191,6 @@ class PdmlMldProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: - PdmlMldProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -195,6 +199,8 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); private: static const uint kMldQuery = 0x82; static const uint kMldV1Query = 0x82; @@ -206,8 +212,6 @@ private: class PdmlIcmp6Protocol : public PdmlProtocol { public: - PdmlIcmp6Protocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -219,6 +223,8 @@ public: virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); private: PdmlIcmpProtocol icmp_; PdmlMldProtocol mld_; @@ -228,8 +234,6 @@ private: class PdmlIgmpProtocol : public PdmlProtocol { public: - PdmlIgmpProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -240,6 +244,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); private: static const uint kIgmpQuery = 0x11; static const uint kIgmpV1Query = 0x11; @@ -252,8 +258,6 @@ private: class PdmlTcpProtocol : public PdmlProtocol { public: - PdmlTcpProtocol(); - static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, @@ -261,6 +265,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); private: QByteArray options_; QByteArray segmentData_; @@ -269,18 +275,16 @@ private: class PdmlUdpProtocol : public PdmlProtocol { public: - PdmlUdpProtocol(); - static PdmlProtocol* createInstance(); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); }; class PdmlTextProtocol : public PdmlProtocol { public: - PdmlTextProtocol(); - static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, @@ -291,6 +295,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlTextProtocol(); private: enum ContentType { kUnknownContent, diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp index 5706798..cb39a4f 100644 --- a/common/pdmlprotocol.cpp +++ b/common/pdmlprotocol.cpp @@ -35,11 +35,6 @@ PdmlProtocol* PdmlProtocol::createInstance() return new PdmlProtocol(); } -QString PdmlProtocol::pdmlProtoName() const -{ - return pdmlProtoName_; -} - int PdmlProtocol::ostProtoId() const { return ostProtoId_; diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h index 0e99b9f..412f588 100644 --- a/common/pdmlprotocol.h +++ b/common/pdmlprotocol.h @@ -27,17 +27,13 @@ along with this program. If not, see #include #include -// TODO: add const where possible - class PdmlProtocol { public: - PdmlProtocol(); // TODO: make private virtual ~PdmlProtocol(); static PdmlProtocol* createInstance(); - QString pdmlProtoName() const; int ostProtoId() const; bool hasField(QString name) const; int fieldId(QString name) const; @@ -59,7 +55,8 @@ public: OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: - QString pdmlProtoName_; // TODO: needed? duplicated in protocolMap_ + PdmlProtocol(); + int ostProtoId_; QMap fieldMap_; }; From 0274fd4479164695aa35ecec5f2503f69bdde855 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 11 Apr 2011 21:56:21 +0530 Subject: [PATCH 158/294] PCAP/PDML import: reverting some temporary code changes --- .hgignore | 2 -- common/pdmlreader.h | 1 - server/pcapport.cpp | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.hgignore b/.hgignore index c268bf1..cd5490f 100644 --- a/.hgignore +++ b/.hgignore @@ -31,5 +31,3 @@ version.cpp # ctags tags - -pcaps\* diff --git a/common/pdmlreader.h b/common/pdmlreader.h index 29aa1ac..7de3918 100644 --- a/common/pdmlreader.h +++ b/common/pdmlreader.h @@ -29,7 +29,6 @@ class PcapFileFormat; class PdmlReader : public QObject, public QXmlStreamReader { Q_OBJECT - //friend class PdmlUnknownProtocol; public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); diff --git a/server/pcapport.cpp b/server/pcapport.cpp index fa2a11e..683d462 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -324,7 +324,7 @@ void PcapPort::PortTransmitter::run() // 'stats callback' function so that both Rx and Tx stats are updated // together - const int kSyncTransmit = 0; // temp mask, shd be 1; + const int kSyncTransmit = 1; int i; qDebug("sendQueueList_.size = %d", sendQueueList_.size()); From 486fe5dcac9ef94ef62b5a271e6e7b1a8f558668 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 12 Apr 2011 20:14:57 +0530 Subject: [PATCH 159/294] PCAP/PDML Import: Reworked the test code to use the Ostinato QSettings --- test/main.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++--------- test/test.pro | 2 +- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/test/main.cpp b/test/main.cpp index c48a9e3..f81f118 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,36 +3,47 @@ #include "pcapfileformat.h" #include "protocol.pb.h" #include "protocolmanager.h" +#include "settings.h" #include +#include +#include #include extern ProtocolManager *OstProtocolManager; -int main(int argc, char* argv[]) +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int argc, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) { bool isOk; - QCoreApplication app(argc, argv); QString error; - if (argc != 2) + if (argc != 3) { - printf("%s \n", argv[0]); - exit(255); + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; } - OstProtocolManager = new ProtocolManager(); - OstProto::StreamConfigList streams; - QString inFile(argv[1]); - - OstProtocolManager = new ProtocolManager(); - - OstProtoLib::setExternalApplicationPaths( - "c:/Program Files/Wireshark/Tshark.exe", - "d:/srivatsp/projects/ostinato/pdml/bin/gzip.exe", - "d:/srivatsp/projects/ostinato/pdml/bin/diff.exe", - "d:/srivatsp/projects/ostinato/pdml/bin/gawk.exe"); + QString inFile(argv[2]); isOk = pcapFileFormat.openStreams(inFile, streams, error); if (!error.isEmpty()) @@ -42,7 +53,57 @@ int main(int argc, char* argv[]) } if (!isOk) - exit(1); + return 1; return 0; } + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/test.pro b/test/test.pro index 828df55..8dbe0cb 100644 --- a/test/test.pro +++ b/test/test.pro @@ -1,7 +1,7 @@ TEMPLATE = app CONFIG += qt console QT += xml network script -INCLUDEPATH += "../rpc/" "../common/" +INCLUDEPATH += "../rpc/" "../common/" "../client" win32 { LIBS += -lwpcap -lpacket CONFIG(debug, debug|release) { From 84544559f247fc8066c6a07c647b4b3ec5978af8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 13 Apr 2011 20:57:10 +0530 Subject: [PATCH 160/294] PCAP/PDML import: final cleanup; renamed pdml_p.* to pdmlprotocols.* --- common/ostproto.pro | 4 +- common/{pdml_p.cpp => pdmlprotocols.cpp} | 61 ++++++++++-------------- common/{pdml_p.h => pdmlprotocols.h} | 4 +- common/pdmlreader.cpp | 2 +- 4 files changed, 29 insertions(+), 42 deletions(-) rename common/{pdml_p.cpp => pdmlprotocols.cpp} (97%) rename common/{pdml_p.h => pdmlprotocols.h} (99%) diff --git a/common/ostproto.pro b/common/ostproto.pro index ee738e1..f0563fc 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -64,8 +64,8 @@ HEADERS += \ pcapfileformat.h \ pdmlfileformat.h \ pdmlprotocol.h \ + pdmlprotocols.h \ pdmlreader.h \ - pdml_p.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ @@ -109,8 +109,8 @@ SOURCES += \ pcapfileformat.cpp \ pdmlfileformat.cpp \ pdmlprotocol.cpp \ + pdmlprotocols.cpp \ pdmlreader.cpp \ - pdml_p.cpp \ protocolmanager.cpp \ protocollist.cpp \ protocollistiterator.cpp \ diff --git a/common/pdml_p.cpp b/common/pdmlprotocols.cpp similarity index 97% rename from common/pdml_p.cpp rename to common/pdmlprotocols.cpp index a36a056..37a1ede 100644 --- a/common/pdml_p.cpp +++ b/common/pdmlprotocols.cpp @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include "pdml_p.h" +#include "pdmlprotocols.h" #include "arp.pb.h" #include "eth2.pb.h" @@ -462,6 +462,7 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); dot3->set_is_override_length(true); } +#if 0 else if (name == "eth.trailer") { QByteArray trailer = QByteArray::fromHex( @@ -479,6 +480,7 @@ void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, stream->mutable_core()->mutable_name()->append(trailer.constData(), trailer.size()); } +#endif } @@ -615,11 +617,11 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, { OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); - ip4->set_is_override_ver(true); // FIXME - ip4->set_is_override_hdrlen(true); // FIXME - ip4->set_is_override_totlen(true); // FIXME - ip4->set_is_override_proto(true); // FIXME - ip4->set_is_override_cksum(true); // FIXME + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); if (options_.size()) { @@ -689,9 +691,9 @@ void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, { OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); - ip6->set_is_override_version(true); // FIXME - ip6->set_is_override_payload_length(true); // FIXME - ip6->set_is_override_next_header(true); // FIXME + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); } @@ -1120,19 +1122,21 @@ void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); else if (name == "") { - if (attributes.value("show").toString().startsWith("TCP segment data")) - { - segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - stream->mutable_core()->mutable_name()->insert(0, - segmentData_.constData(), segmentData_.size()); - } - else if (attributes.value("show").toString().startsWith("Acknowledgement number")) + if (attributes.value("show").toString().startsWith("Acknowledgement number")) { bool isOk; OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif } } @@ -1143,10 +1147,10 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, qDebug("Tcp: post\n"); - tcp->set_is_override_src_port(true); // FIXME - tcp->set_is_override_dst_port(true); // FIXME - tcp->set_is_override_hdrlen(true); // FIXME - tcp->set_is_override_cksum(true); // FIXME + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); if (options_.size()) { @@ -1162,23 +1166,6 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, hexDump->set_pad_until_end(false); options_.resize(0); } - -#if 0 - if (segmentData_.size()) - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - hexDump->mutable_content()->append(segmentData_.constData(), - segmentData_.size()); - hexDump->set_pad_until_end(false); - segmentData_.resize(0); - } -#endif } // ---------------------------------------------------------- // diff --git a/common/pdml_p.h b/common/pdmlprotocols.h similarity index 99% rename from common/pdml_p.h rename to common/pdmlprotocols.h index 1824d9d..95a75c0 100644 --- a/common/pdml_p.h +++ b/common/pdmlprotocols.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#ifndef _PDML_P_H -#define _PDML_P_H +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H #include "pdmlprotocol.h" diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp index f2ac300..c4ea3f0 100644 --- a/common/pdmlreader.cpp +++ b/common/pdmlreader.cpp @@ -24,7 +24,7 @@ along with this program. If not, see #include "pcapfileformat.h" #include "streambase.h" -#include "pdml_p.h" // TODO: Remove +#include "pdmlprotocols.h" PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { From 44fdf844b635d8332614a8f2d06990a4a0bbe735 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 13 Apr 2011 21:20:07 +0530 Subject: [PATCH 161/294] PDML/PCAP Import: Fixed gcc4 warnings --- common/pdmlprotocols.cpp | 2 +- test/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp index 37a1ede..31e1303 100644 --- a/common/pdmlprotocols.cpp +++ b/common/pdmlprotocols.cpp @@ -1116,7 +1116,7 @@ PdmlProtocol* PdmlTcpProtocol::createInstance() void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { if (name == "tcp.options") options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); diff --git a/test/main.cpp b/test/main.cpp index f81f118..d5e8af1 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -20,7 +20,7 @@ QString kDiffPathDefaultValue; QString kAwkPathDefaultValue; #endif -int usage(int argc, char* argv[]) +int usage(int /*argc*/, char* argv[]) { printf("usage:\n"); printf("%s \n", argv[0]); From 12aea5986764722ad0e24dd944eaf40b4db3c891 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 14 Apr 2011 22:38:39 +0530 Subject: [PATCH 162/294] At exit Ostinato main window geometry and layout is saved and restored next time you open Ostinato --- client/mainwindow.cpp | 19 +++++++++++++++++++ client/settings.h | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 623624f..03a16ab 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -27,6 +27,7 @@ along with this program. If not, see #include "portstatswindow.h" #include "portswindow.h" #include "preferences.h" +#include "settings.h" #include "ui_about.h" #include @@ -62,7 +63,13 @@ MainWindow::MainWindow(QWidget *parent) portsWindow = new PortsWindow(pgl, this); statsWindow = new PortStatsWindow(pgl, this); portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); setupUi(this); @@ -73,6 +80,14 @@ MainWindow::MainWindow(QWidget *parent) portsDock->setWidget(portsWindow); addDockWidget(Qt::TopDockWidgetArea, portsDock); + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); #if 0 @@ -89,6 +104,10 @@ MainWindow::~MainWindow() localServer_->terminate(); localServer_->waitForFinished(); delete localServer_; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); } void MainWindow::on_actionPreferences_triggered() diff --git a/client/settings.h b/client/settings.h index 2f0666c..d76bb47 100644 --- a/client/settings.h +++ b/client/settings.h @@ -74,6 +74,13 @@ const QString kAwkPathDefaultValue("/usr/bin/awk"); const QString kAwkPathDefaultValue("/usr/bin/awk"); #endif + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + #endif From 073f961d0baa2b3b955780c95900fe1b5ce0e6bd Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 14 Apr 2011 22:40:44 +0530 Subject: [PATCH 163/294] Bumped Copyright to include 2011 in About Dialog --- client/about.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/about.ui b/client/about.ui index 34cd66c..56cf1a5 100644 --- a/client/about.ui +++ b/client/about.ui @@ -94,7 +94,7 @@ - Copyright (c) 2007-2010 Srivats P. + Copyright (c) 2007-2011 Srivats P. Qt::AlignCenter From d05821da66733789f1f958c5a89130187b991cf5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 14 Apr 2011 22:46:27 +0530 Subject: [PATCH 164/294] StreamConfigDialog: renamed Goto Stream X as Goto First till the Goto Stream X is implemented --- client/streamconfigdialog.cpp | 4 ++-- client/streamconfigdialog.ui | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index ad81cbf..cb7579b 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2011 Srivats P. This file is part of "Ostinato" @@ -161,7 +161,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, pbPrev->setDisabled(true); pbNext->setDisabled(true); //! \todo Support Goto Stream Id - leStreamId->setDisabled(true); + leStreamId->setHidden(true); disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); //! \todo Support Continuous Mode rbModeContinuous->setDisabled(true); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 1f580a3..4f232dd 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -896,7 +896,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - Goto Stream + Goto First From e0efac0f59e83f75281b6364695bc26637c6cd4f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 17 Apr 2011 10:51:08 +0530 Subject: [PATCH 165/294] Removed the test code building from top level project file. Test code needs to be built manually --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 ++++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 83 ++ client/mainwindow.cpp | 135 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 ++++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 85 ++ client/ostinato.qrc | 37 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 377 +++++++ client/port.h | 130 +++ client/portgroup.cpp | 837 +++++++++++++++ client/portgroup.h | 145 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 338 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 ++++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 315 ++++++ client/portstatsmodel.h | 141 +++ client/portstatswindow.cpp | 180 ++++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 ++++ client/portswindow.cpp | 565 ++++++++++ client/portswindow.h | 80 ++ client/portswindow.ui | 248 +++++ client/preferences.cpp | 119 +++ client/preferences.h | 45 + client/preferences.ui | 226 ++++ client/settings.h | 86 ++ client/stream.cpp | 79 ++ client/stream.h | 42 + client/streamconfigdialog.cpp | 1011 ++++++++++++++++++ client/streamconfigdialog.h | 137 +++ client/streamconfigdialog.ui | 1353 ++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 283 ++++++ client/streammodel.h | 78 ++ common/abstractfileformat.cpp | 127 +++ common/abstractfileformat.h | 91 ++ common/abstractprotocol.cpp | 825 +++++++++++++++ common/abstractprotocol.h | 162 +++ common/arp.cpp | 954 +++++++++++++++++ common/arp.h | 120 +++ common/arp.proto | 67 ++ common/arp.ui | 518 ++++++++++ common/comboprotocol.h | 217 ++++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot3.cpp | 234 +++++ common/dot3.h | 79 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/eth2.cpp | 231 +++++ common/eth2.h | 78 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/fileformat.cpp | 446 ++++++++ common/fileformat.h | 60 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 1036 +++++++++++++++++++ common/gmp.h | 162 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 +++++++++++++++ common/hexdump.cpp | 263 +++++ common/hexdump.h | 89 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/icmp.cpp | 594 +++++++++++ common/icmp.h | 117 +++ common/icmp.proto | 44 + common/icmp.ui | 178 ++++ common/igmp.cpp | 449 ++++++++ common/igmp.h | 111 ++ common/igmp.proto | 27 + common/intcombobox.h | 69 ++ common/ip4.cpp | 739 ++++++++++++++ common/ip4.h | 115 +++ common/ip4.proto | 66 ++ common/ip4.ui | 516 ++++++++++ common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip6.cpp | 940 +++++++++++++++++ common/ip6.h | 130 +++ common/ip6.proto | 61 ++ common/ip6.ui | 467 +++++++++ common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/iputils.h | 122 +++ common/ipv4addressdelegate.h | 58 ++ common/ipv6addressdelegate.h | 60 ++ common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 331 ++++++ common/llc.h | 86 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/mac.cpp | 317 ++++++ common/mac.h | 90 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/mld.cpp | 624 ++++++++++++ common/mld.h | 108 ++ common/mld.proto | 27 + common/ostproto.pro | 142 +++ common/ostprotolib.cpp | 55 + common/ostprotolib.h | 42 + common/payload.cpp | 258 +++++ common/payload.h | 84 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/pcapfileformat.cpp | 661 ++++++++++++ common/pcapfileformat.h | 87 ++ common/pcapfileimport.ui | 132 +++ common/pdmlfileformat.cpp | 163 +++ common/pdmlfileformat.h | 42 + common/pdmlprotocol.cpp | 153 +++ common/pdmlprotocol.h | 64 ++ common/pdmlprotocols.cpp | 1357 +++++++++++++++++++++++++ common/pdmlprotocols.h | 313 ++++++ common/pdmlreader.cpp | 533 ++++++++++ common/pdmlreader.h | 75 ++ common/protocol.proto | 235 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 212 ++++ common/protocolmanager.h | 57 ++ common/sample.cpp | 534 ++++++++++ common/sample.h | 102 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/snap.cpp | 307 ++++++ common/snap.h | 82 ++ common/snap.proto | 34 + common/snap.ui | 122 +++ common/streambase.cpp | 489 +++++++++ common/streambase.h | 144 +++ common/svlan.cpp | 68 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/tcp.cpp | 699 +++++++++++++ common/tcp.h | 100 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/textproto.cpp | 295 ++++++ common/textproto.h | 91 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/udp.cpp | 492 +++++++++ common/udp.h | 87 ++ common/udp.proto | 39 + common/udp.ui | 174 ++++ common/userscript.cpp | 609 +++++++++++ common/userscript.h | 181 ++++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/vlan.cpp | 257 +++++ common/vlan.h | 82 ++ common/vlan.proto | 34 + common/vlan.ui | 179 ++++ common/vlanstack.h | 30 + common/vlanstack.proto | 31 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 8 + extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 106 ++ extra/qhexedit2/src/qhexedit.h | 145 +++ extra/qhexedit2/src/qhexedit_p.cpp | 414 ++++++++ extra/qhexedit2/src/qhexedit_p.h | 82 ++ install.pri | 14 + ost.pro | 8 + protobuf.pri | 33 + rpc/pbhelper.h | 170 ++++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 338 ++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 39 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 291 ++++++ rpc/rpcserver.h | 66 ++ server/abstractport.cpp | 250 +++++ server/abstractport.h | 111 ++ server/drone.cpp | 102 ++ server/drone.h | 56 + server/drone.pro | 45 + server/drone.qrc | 5 + server/drone.ui | 190 ++++ server/drone_main.cpp | 49 + server/icons/portgroup.png | Bin 0 -> 667 bytes server/myservice.cpp | 470 +++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 533 ++++++++++ server/pcapport.h | 151 +++ server/portmanager.cpp | 86 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 216 ++++ server/winpcapport.h | 56 + test/main.cpp | 109 ++ test/test.pro | 34 + version.pri | 19 + 262 files changed, 41961 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/comboprotocol.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/ostproto.pro create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h create mode 100644 common/pcapfileimport.ui create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlprotocols.cpp create mode 100644 common/pdmlprotocols.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone.qrc create mode 100644 server/drone.ui create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 test/main.cpp create mode 100644 test/test.pro create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..56cf1a5 --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2011 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
    G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/ostprotolib.h" +#include "../common/protocolmanager.h" +#include "settings.h" + +#include +#include +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; +QMainWindow *mainWindow; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + delete mainWindow; + delete appSettings; + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..03a16ab --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,135 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "settings.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); + statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..c12c781 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,85 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..bd88fd1 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,37 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..1acb94d --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,377 @@ +/* +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 +*/ + +#include "port.h" + +#include "abstractfileformat.h" + +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; + +uint Port::mAllocStreamId = 0; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + bool ret = false; + QDialog *optDialog; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); + OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); + + if (fmt == NULL) + goto _fail; + + if ((optDialog = fmt->openOptionsDialog())) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) + goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); + + if (!append) + { + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + } + + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); + for (int i = 0; i < streams.stream_size(); i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + +_user_cancel: + emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: + ret = true; + +_fail: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} + +bool Port::saveStreams(QString fileName, QString fileType, QString &error) +{ + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); + OstProto::StreamConfigList streams; + + if (fmt == NULL) + goto _fail; + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..275ea61 --- /dev/null +++ b/client/port.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); + +signals: + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..bbea61d --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,837 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://code.google.com/p/ostinato/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, bool isExclusive) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + port->set_is_exclusive_control(isExclusive); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + emit portGroupDataChanged(mPortGroupId); + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..254e731 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,145 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { reconnect = true; rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, bool isExclusive); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..1d13eda --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..92e6a24 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,315 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..d50508b --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,141 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..837fb89 --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,565 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include "abstractfileformat.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +#include +#include +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + //! \todo Hide the Aggregate Box till we add support + frAggregate->setHidden(true); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + scd->exec(); // TODO: chk retval + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& /*previous*/) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + updatePortViewActions(tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + plm->portGroup(current.parent()).modifyPort(current.row(), checked); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; + QString fileName; + QString errorStr; + bool append = true; + bool ret; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + dirName = QFileInfo(fileName).absolutePath(); + +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; + QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need. +#ifdef Q_OS_MAC + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); + + Q_ASSERT(plm->isPort(current)); + +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); + if (fileName.isEmpty()) + goto _exit; + + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..ed93854 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a724c06 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,248 @@ + + PortsWindow + + + + 0 + 0 + 689 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Capacity + + + + + + + + + + Aggr fps + + + + + + + + + + % age + + + + + + + + + + Aggr bps + + + + + + + + + + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..0e54bbd --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,119 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "../common/ostprotolib.h" +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..78109ab --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..d64b4cb --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,226 @@ + + Preferences + + + + 0 + 0 + 400 + 220 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 'wireshark' Path + + + wiresharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + Qt::Vertical + + + + 21 + 61 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..d76bb47 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..a24c971 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,79 @@ +/* +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 +*/ + +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..cb7579b --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1011 @@ +/* +Copyright (C) 2010-2011 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 + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager *OstProtocolManager; + +QRect StreamConfigDialog::lastGeometry; +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setDisabled(true); + pbNext->setDisabled(true); + //! \todo Support Goto Stream Id + leStreamId->setHidden(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + //! \todo Support Continuous Mode + rbModeContinuous->setDisabled(true); + + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + AbstractProtocol *p; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); + + tbProtocolData->show(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); + leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if (!mpStream->preflightCheck(log)) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastGeometry = geometry(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..8321eba --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,137 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..4f232dd --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1353 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto First + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + Qt::Horizontal + + + + 41 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + leNumPackets + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bursts/Sec + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Gaps + + + false + + + false + + + + + + + + + icons/gaps.png + + + + + + + ISG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leNumPackets + + + + + + + IBG + + + leNumPackets + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
    dumpview.h
    + 1 +
    +
    + + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 558 + 453 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 169 + 207 + + + 169 + 207 + + + + + rbSendPackets + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 125 + 207 + + + 125 + 207 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 125 + 207 + + + 156 + 207 + + + + +
    diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..19942fd --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,283 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + return (int) StreamMaxFields; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..9c7e1aa --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamName, + StreamStatus, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..15271d7 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ + stop_ = false; +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..1f8447d --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class AbstractFileFormat : public QThread +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif + diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..ef80783 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,825 @@ +/* +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 +*/ + +#include + +#include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..07350cc --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,162 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..f23b6b6 --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,954 @@ +/* +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 +*/ + +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + return true; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..677b73a --- /dev/null +++ b/common/arp.h @@ -0,0 +1,120 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
    diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..29cf71d --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..ea5f6a5 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,234 @@ +/* +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 +*/ + +#include "dot3.h" +#include "streambase.h" + +#include +#include +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Dot3Protocol::~Dot3Protocol() +{ + delete configForm; +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +QWidget* Dot3Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideLength->setChecked( + fieldData(dot3_is_override_length, FieldValue).toBool()); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); +} + +void Dot3Protocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(dot3_is_override_length, + configForm->cbOverrideLength->isChecked()); + setFieldData(dot3_length,configForm->leLength->text()); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..403a2ce --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,79 @@ +/* +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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..73038b7 --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,231 @@ +/* +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 +*/ + +#include +#include + +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Eth2Protocol::~Eth2Protocol() +{ + delete configForm; +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideType->setChecked( + fieldData(eth2_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(eth2_is_override_type, + configForm->cbOverrideType->isChecked()); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5694339 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + +public: + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..aada8b1 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,446 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + Q_ASSERT(meta.data().format_version_minor() == kFileFormatVersionMinor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..0204906 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include "fileformat.pb.h" + +class FileFormat : public AbstractFileFormat +{ +public: + FileFormat(); + ~FileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 1; + static const uint kFileFormatVersionRevision = 3; +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..7bc7ced --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,1036 @@ +/* +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 +*/ + +#include "gmp.h" + +#include +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +GmpProtocol::~GmpProtocol() +{ + delete configForm; +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +void GmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); + // XXX: configForm->maxResponseTime set by subclass + configForm->overrideChecksum->setChecked( + fieldData(kIsOverrideChecksum, FieldValue).toBool()); + configForm->checksum->setText(uintToHexStr( + fieldData(kChecksum, FieldValue).toUInt(), 2)); + + configForm->groupAddress->setText( + fieldData(kGroupAddress, FieldValue).toString()); + configForm->groupMode->setCurrentIndex( + fieldData(kGroupMode, FieldValue).toUInt()); + configForm->groupCount->setText( + fieldData(kGroupCount, FieldValue).toString()); + configForm->groupPrefix->setText( + fieldData(kGroupPrefix, FieldValue).toString()); + + configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); + configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); + configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + + QStringList sl = fieldData(kSources, FieldValue).toStringList(); + configForm->sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + configForm->sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + configForm->overrideSourceCount->setChecked( + fieldData(kIsOverrideSourceCount, FieldValue).toBool()); + configForm->sourceCount->setText( + fieldData(kSourceCount, FieldValue).toString()); + + QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); + configForm->groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(configForm->groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + configForm->groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + configForm->overrideGroupRecordCount->setChecked( + fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); + configForm->groupRecordCount->setText( + fieldData(kGroupRecordCount, FieldValue).toString()); + +} + +void GmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + configForm->update(); + + setFieldData(kType, configForm->msgTypeCombo->currentValue()); + // XXX: configForm->maxResponseTime handled by subclass + setFieldData(kIsOverrideChecksum, + configForm->overrideChecksum->isChecked()); + setFieldData(kChecksum, + configForm->checksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kGroupAddress, configForm->groupAddress->text()); + setFieldData(kGroupMode, configForm->groupMode->currentIndex()); + setFieldData(kGroupCount, configForm->groupCount->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); + + setFieldData(kSFlag, configForm->sFlag->isChecked()); + setFieldData(kQrv, configForm->qrv->text()); + setFieldData(kQqic, configForm->qqi->text()); + + QStringList list; + for (int i = 0; i < configForm->sourceList->count(); i++) + list.append(configForm->sourceList->item(i)->text()); + setFieldData(kSources, list); + + // sourceCount should be AFTER sources + setFieldData(kIsOverrideSourceCount, + configForm->overrideSourceCount->isChecked()); + setFieldData(kSourceCount, configForm->sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < configForm->groupList->count(); i++) + { + QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + setFieldData(kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + setFieldData(kIsOverrideGroupRecordCount, + configForm->overrideGroupRecordCount->isChecked()); + setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..97dc5fd --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,162 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "gmp.pb.h" +#include "ui_gmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ +class GmpProtocol; + +class GmpConfigForm : public QWidget, public Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + void update(); +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +class GmpProtocol : public AbstractProtocol +{ +public: + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + OstProto::Gmp data; + GmpConfigForm *configForm; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
    diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..f579430 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,263 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +#include + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +HexDumpProtocol::~HexDumpProtocol() +{ + delete configForm; +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + +QWidget* HexDumpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new HexDumpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void HexDumpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hexEdit->setData( + fieldData(hexDump_content, FieldValue).toByteArray()); + configForm->padUntilEnd->setChecked( + fieldData(hexDump_pad_until_end, FieldValue).toBool()); +} + +void HexDumpProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(hexDump_content, configForm->hexEdit->data()); + setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..f5b6932 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "hexdump.pb.h" +#include "ui_hexdump.h" + +#include "abstractprotocol.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpConfigForm : public QWidget, public Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +class HexDumpProtocol : public AbstractProtocol +{ +public: + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + OstProto::HexDump data; + HexDumpConfigForm *configForm; + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
    qhexedit.h
    + 1 +
    +
    + + +
    diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..e7f6267 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,594 @@ +/* +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 +*/ + + +#include "icmp.h" + +#include +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->versionGroup->button(icmpVersion())->click(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..a3fc296 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,117 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + QButtonGroup *versionGroup; + + IcmpConfigForm(QWidget *parent = 0); +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
    diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..046f675 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,449 @@ +/* +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 +*/ + +#include "igmp.h" + +#include "ipv4addressdelegate.h" +#include "iputils.h" + +#include +#include + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* IgmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new IgmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IgmpProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kRsvdMrtCode, FieldValue).toString()); +} + +void IgmpProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..6ee9ad3 --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,111 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "igmp.pb.h" +#include "gmp.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..197cc8e --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,739 @@ +/* +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 +*/ + +#include +#include + +#include "ip4.h" + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Ip4Protocol::~Ip4Protocol() +{ + delete configForm; +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip4Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + + configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); + + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + configWidget(); + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + + data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..006d9ca --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,115 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..fe5d29b --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,940 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..1856bc8 --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,130 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..5adecf1 --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,331 @@ +/* +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 +*/ + +#include +#include + +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +LlcProtocol::~LlcProtocol() +{ + delete configForm; +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + + +QWidget* LlcProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configWidget(); + + configForm->cbOverrideDsap->setChecked( + fieldData(llc_is_override_dsap, FieldValue).toBool()); + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideSsap->setChecked( + fieldData(llc_is_override_ssap, FieldValue).toBool()); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideControl->setChecked( + fieldData(llc_is_override_ctl, FieldValue).toBool()); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(llc_is_override_dsap, + configForm->cbOverrideDsap->isChecked()); + setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ssap, + configForm->cbOverrideSsap->isChecked()); + setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ctl, + configForm->cbOverrideControl->isChecked()); + setFieldData(llc_ctl, + configForm->leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..808d442 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..040f870 --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,317 @@ +/* +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 +*/ + +#include +#include + +#include "mac.h" + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +MacProtocol::~MacProtocol() +{ + delete configForm; +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + + +QWidget* MacProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..48d0e35 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,90 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..4c373ae --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,624 @@ +/* +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 +*/ + +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" +#include "iputils.h" + +#include +#include + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j+8)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* MldProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new MldConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void MldProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kMldMrt, FieldValue).toString()); +} + +void MldProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kMldMrt, configForm->maxResponseTime->text()); +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..bc2d95e --- /dev/null +++ b/common/mld.h @@ -0,0 +1,108 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "mld.pb.h" +#include "gmp.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..f0563fc --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,142 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network script xml +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf +FORMS += \ + pcapfileimport.ui \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + hexdump.ui \ + sample.ui +PROTOS += \ + protocol.proto \ + fileformat.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto +HEADERS += \ + ostprotolib.h \ + abstractprotocol.h \ + comboprotocol.h \ + abstractfileformat.h \ + fileformat.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + arp.h \ + ip4.h \ + ip6.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + ip6over4.h \ + ip4over6.h \ + ip4over4.h \ + ip6over6.h \ + icmp.h \ + gmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + userscript.h \ + hexdump.h \ + sample.h +SOURCES += \ + ostprotolib.cpp \ + abstractprotocol.cpp \ + crc32c.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + icmp.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + userscript.cpp \ + hexdump.cpp \ + sample.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..e46dc8c --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} + diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..d5fb2e5 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,258 @@ +/* +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 +*/ + +#include +#include + +//#include "../client/stream.h" +#include "payload.h" +#include "streambase.h" + + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +PayloadProtocol::~PayloadProtocol() +{ + delete configForm; +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + + +QWidget* PayloadProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void PayloadProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..2fd32d2 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,84 @@ +/* +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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..bc0ccd6 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,661 @@ +/* +Copyright (C) 2011 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 "pcapfileformat.h" + +#include "pdmlreader.h" +#include "ostprotolib.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; +const quint32 kDltEthernet = 1; + +PcapFileFormat pcapFileFormat; + +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + +PcapFileFormat::PcapFileFormat() +{ + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + + importDialog_ = NULL; +} + +PcapFileFormat::~PcapFileFormat() +{ + delete importDialog_; +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QTemporaryFile file2; + quint32 magic; + uchar gzipMagic[2]; + int len; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; + + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; + + emit status("Decompressing..."); + emit target(0); + + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + gzip.start(OstProtoLib::gzipPath(), + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + + fd_ >> magic; + + qDebug("magic = %08x", magic); + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); + else + fd_.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif + + pktBuf.resize(fileHdr.snapLen); + + if (importOptions_.value("ViaPdml").toBool()) + { + QProcess tshark; + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + diff.start(OstProtoLib::diffPath(), + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff. Check path in Preferences.\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + + goto _exit; + } + +_non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage + pktCount = 1; + while (!fd_.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + readPacket(pktHdr, pktBuf); + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; + pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; + } + + isOk = true; + goto _exit; + +_user_cancel: + isOk = true; + goto _exit; + +_diff_fail: + goto _exit; + +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +#if 0 +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + file.close(); + return isOk; +} + +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd_.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = kDltEthernet; + + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + emit status("Writing Packets..."); + emit target(streams.stream_size()); + + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += 1000000/s.packetRate(); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } + + emit progress(i); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..064aaf1 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "abstractfileformat.h" +#include "ui_pcapfileimport.h" + +#include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; + +class PdmlReader; +class PcapFileFormat : public AbstractFileFormat +{ + friend class PdmlReader; + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + virtual QDialog* openOptionsDialog(); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..9ba1f2d --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,163 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" + +#include "ostprotolib.h" +#include "pdmlreader.h" + +#include +#include + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } + + goto _exit; + +_open_fail: + isOk = false; + +_user_cancel: +_exit: + delete reader; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; +} + +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + xml.readNext(); + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..e05026a --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "abstractfileformat.h" + +class PdmlFileFormat : public AbstractFileFormat +{ +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..cb39a4f --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,153 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +const int kBaseHex = 16; + +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +PdmlProtocol::~PdmlProtocol() +{ +} + +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..412f588 --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,64 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +class PdmlProtocol +{ +public: + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlProtocol(); + + int ostProtoId_; + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp new file mode 100644 index 0000000..31e1303 --- /dev/null +++ b/common/pdmlprotocols.cpp @@ -0,0 +1,1357 @@ +/* +Copyright (C) 2011 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 "pdmlprotocols.h" + +#include "arp.pb.h" +#include "eth2.pb.h" +#include "dot3.pb.h" +#include "gmp.pb.h" +#include "hexdump.pb.h" +#include "llc.pb.h" +#include "mac.pb.h" +#include "icmp.pb.h" +#include "igmp.pb.h" +#include "ip4.pb.h" +#include "ip6.pb.h" +#include "mld.pb.h" +#include "sample.pb.h" +#include "snap.pb.h" +#include "svlan.pb.h" +#include "tcp.pb.h" +#include "textproto.pb.h" +#include "udp.pb.h" +#include "vlan.pb.h" + +#include +#include + +const int kBaseHex = 16; + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +PdmlProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + endPos_ = pos; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + // Skipped field(s) at end? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), + pos, expPos_, endPos_); + + // Skipped field? Pad with zero! + if ((pos > expPos_) && (expPos_ < endPos_)) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ +} + +PdmlProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ +} + +PdmlProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} + + +// ---------------------------------------------------------- // +// PdmlSvlanProtocol // +// ---------------------------------------------------------- // + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlVlanProtocol // +// ---------------------------------------------------------- // + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlEthProtocol // +// ---------------------------------------------------------- // + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + + +// ---------------------------------------------------------- // +// PdmlLlcProtocol // +// ---------------------------------------------------------- // + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + + +// ---------------------------------------------------------- // +// PdmlArpProtocol // +// ---------------------------------------------------------- // + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + + +// ---------------------------------------------------------- // +// PdmlIp4Protocol // +// ---------------------------------------------------------- // + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlIp6Protocol // +// ---------------------------------------------------------- // + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + + +// ---------------------------------------------------------- // +// PdmlIcmpProtocol // +// ---------------------------------------------------------- // + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + +// ---------------------------------------------------------- // +// PdmlIcmp6Protocol // +// ---------------------------------------------------------- // + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + + +// ---------------------------------------------------------- // +// PdmlIgmpProtocol // +// ---------------------------------------------------------- // + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + + +// ---------------------------------------------------------- // +// PdmlMldProtocol // +// ---------------------------------------------------------- // + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + + +// ---------------------------------------------------------- // +// PdmlTcpProtocol // +// ---------------------------------------------------------- // + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlUdpProtocol // +// ---------------------------------------------------------- // + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + + +// ---------------------------------------------------------- // +// PdmlTextProtocol // +// ---------------------------------------------------------- // + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h new file mode 100644 index 0000000..95a75c0 --- /dev/null +++ b/common/pdmlprotocols.h @@ -0,0 +1,313 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H + +#include "pdmlprotocol.h" + +class PdmlUnknownProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlGenInfoProtocol(); + +}; + +class PdmlFrameProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); +}; + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..c4ea3f0 --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,533 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdmlprotocols.h" + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + QStringList creator; + + Q_ASSERT(isStartElement() && name() == "pdml"); + + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..7de3918 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + bool isMldSupport_; + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..5e3f208 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,235 @@ +/* +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 +*/ + +package OstProto; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 packets_per_sec = 7 [default = 1]; + optional uint32 bursts_per_sec = 8 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..e91f270 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,212 @@ +/* +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 +*/ + +#include "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" +#include "vlan.h" +#include "vlanstack.h" +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" +#include "ip4over4.h" +#include "ip6over6.h" +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" +#include "textproto.h" +#include "userscript.h" +#include "hexdump.h" +#include "sample.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..07b0604 --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,57 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..2a79660 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,534 @@ +/* +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 +*/ + +#include + +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +SampleProtocol::~SampleProtocol() +{ + delete configForm; +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +QWidget* SampleProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ +void SampleProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ +void SampleProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..91e6573 --- /dev/null +++ b/common/sample.h @@ -0,0 +1,102 @@ +/* +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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "sample.pb.h" +#include "ui_sample.h" + +#include "abstractprotocol.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + +public: + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..bdb80c3 --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,307 @@ +/* +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 +*/ + +#include +#include + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +SnapProtocol::~SnapProtocol() +{ + delete configForm; +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} + +QWidget* SnapProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideOui->setChecked( + fieldData(snap_is_override_oui, FieldValue).toBool()); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + + configForm->cbOverrideType->setChecked( + fieldData(snap_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + configWidget(); + + setFieldData(snap_is_override_oui, + configForm->cbOverrideOui->isChecked()); + setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + + setFieldData(snap_is_override_type, + configForm->cbOverrideType->isChecked()); + setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..daba802 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + +public: + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..b9dea25 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,489 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +quint32 StreamBase::packetRate() const +{ + return (quint32) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(quint32 packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +quint32 StreamBase::burstRate() const +{ + return (quint32) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(quint32 burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..7afbbd9 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,144 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + quint32 packetRate() const; + bool setPacketRate(quint32 packetsPerSec); + + quint32 burstRate() const; + bool setBurstRate(quint32 burstsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..893671d --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,68 @@ +/* +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 +*/ + +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..02faa6a --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,699 @@ +/* +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 +*/ + +#include +#include + +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +TcpProtocol::~TcpProtocol() +{ + delete configForm; +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* TcpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); + + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); + + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); + + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); + + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); + + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + int ff = 0; + + configWidget(); + + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); + + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); + + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); + + setFieldData(tcp_window, configForm->leTcpWindow->text()); + + setFieldData(tcp_cksum, configForm->leTcpCksum->text()); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); + + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + setFieldData(tcp_flags, ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..265937a --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..9834530 --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,295 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->eolCombo->setCurrentIndex( + fieldData(textProto_eol, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..1ec5fc0 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..f6996aa --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProtocol + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + +
    diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..8856751 --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,492 @@ +/* +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 +*/ + +#include +#include + +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +UdpProtocol::~UdpProtocol() +{ + delete configForm; +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +QWidget* UdpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); + + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); + + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); + + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); + + setFieldData(udp_cksum, configForm->leUdpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..623e999 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,87 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..fa084f1 --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,609 @@ +/* +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 +*/ + +#include "userscript.h" + +#include + +// +// -------------------- UserScriptConfigForm -------------------- +// + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), protocol_(protocol) +{ + setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } + updateStatus(); +} + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + configForm = NULL; + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..4ef0d91 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,181 @@ +/* +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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; +}; + + + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + void updateStatus(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + + +class UserScriptProtocol : public AbstractProtocol +{ + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..95b2304 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,257 @@ +/* +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 +*/ + +#include + +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +VlanProtocol::~VlanProtocol() +{ + delete configForm; +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..728572b --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +protected: + OstProto::Vlan data; + +public: + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..3e0326d --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,179 @@ + + Vlan + + + + 0 + 0 + 268 + 96 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..c7b9989 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/qhexedit.h \ + src/qhexedit_p.h + +SOURCES = src/qhexedit.cpp \ + src/qhexedit_p.cpp diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..a295110 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,106 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(currentAddress(int)), this, SIGNAL(currentAddress(int))); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..2b94581 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,145 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.4.3 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text data. +There are sip configuration files included, so it is easy to create bindings +for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the +mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f) +you will change the data. Changed data is highlighted and can be accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and +insert data. In this case the size of data() increases. It is also possible to delete +bytes under the cursor, here the size of data decreases. + +There are some limitations: The size of data has in general to be below 10 megabytes, +otherwise the scroll sliders ard not shown and you can't scroll any more. Copy and +paste functionality is perhaps a subject of a later release. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwritem mode the user will overwrite existing data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + */ + void remove(int pos, int len=1); + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + void setFont(const QFont &); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + /*! \endcond docNever */ + +public slots: + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddress(int address); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..30f0660 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,414 @@ +#include + +#include "qhexedit_p.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setAddressAreaColor(QColor(Qt::lightGray).lighter(110)); + setHighlightingColor(QColor(Qt::yellow).lighter(160)); + + setFont(QFont("Mono", 10)); + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + + _cursorTimer.setInterval(500); + _cursorTimer.start(); + + setFocusPolicy(Qt::StrongFocus); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _addressOffset = offset; + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _addressOffset; +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _data = data; + _originalData = data; + adjust(); + setCursorPos(0); + setFocus(); +} + +QByteArray QHexEditPrivate::data() +{ + return _data; +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + if (overwriteMode != _overwriteMode) + { + emit overwriteModeChanged(overwriteMode); + _overwriteMode = overwriteMode; + adjust(); + } +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _originalData.insert(i, ba); +} + +void QHexEditPrivate::insert(int i, char ch) +{ + _data.insert(i, ch); + _originalData.insert(i, ch); +} + +void QHexEditPrivate::remove(int index, int len) +{ + _data.remove(index, len); + _originalData.remove(index, len); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + if ((addressWidth >= 0) and (addressWidth<=6)) + { + _addressNumbers = addressWidth; + adjust(); + setCursorPos(_cursorPosition); + } +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + bool down = false; + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + // calc address + + + // insert char + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + adjust(); + } + QByteArray hexValue = _data.mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + _data.replace(posBa, 1, QByteArray().fromHex(hexValue)); + emit dataChanged(); + + setCursorPos(_cursorPosition + 1); + down = true; + } + + // delete char + if (event->matches(QKeySequence::Delete)) + remove(posBa); + if (event->key() == Qt::Key_Backspace) + { + remove(posBa - 1); + setCursorPos(_cursorPosition - 2); + } + + // handle other function keys + if (event->key() == Qt::Key_Insert) + setOverwriteMode(!_overwriteMode); + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + down = true; + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + setCursorPos(_cursorPosition - 1); + if (event->matches(QKeySequence::MoveToStartOfLine)) + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + if (event->matches(QKeySequence::MoveToEndOfLine)) + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + if (event->matches(QKeySequence::MoveToPreviousLine)) + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToPreviousPage)) + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + if (event->matches(QKeySequence::MoveToStartOfDocument)) + setCursorPos(0); + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + down = true; + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_data.size() * 2); + down = true; + } + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + down = true; + } + + // when we move downwards, we have to go a little further + if (down) + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3 + _charHeight); + else + _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3); + update(); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + setCursorPos(event->pos()); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _data.size()) + lastLineIdx = _data.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _addressOffset, _realAddressNumbers, 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + painter.setBackground(highLighted); + painter.setBackgroundMode(Qt::TransparentMode); + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + // hilight diff bytes + if (_highlighting) + { + int posBa = lineIdx + colIdx; + if (posBa >= _originalData.size()) + painter.setBackgroundMode(Qt::TransparentMode); + else + if (_data[posBa] == _originalData[posBa]) + painter.setBackgroundMode(Qt::TransparentMode); + else + painter.setBackgroundMode(Qt::OpaqueMode); + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + } + } + painter.setBackgroundMode(Qt::TransparentMode); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray ascii = _data.mid(lineIdx, BYTES_PER_LINE); + for (int idx=0; idx < ascii.size(); idx++) + if (((char)ascii[idx] < 0x20) or ((char)ascii[idx] > 0x7e)) + ascii[idx] = '.'; + painter.drawText(_xPosAscii, yPos, ascii); + } + } + + // paint cursor + if ((_data.size() > 0) and _blink) + painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText)); +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_data.size() * 2 - 1)) + position = _data.size() * 2 - 1; + } else { + if (position > (_data.size() * 2)) + position = _data.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddress(_cursorPosition/2); +} + +void QHexEditPrivate::setCursorPos(QPoint pos) +{ + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = (pos.y() / _charHeight) * 2 * BYTES_PER_LINE; + setCursorPos(x + y); + } +} + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _realAddressNumbers *_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + if (_overwriteMode) + _cursorWidth = _charWidth; + else + _cursorWidth = 2; + _cursorHeight = _charHeight - 3; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_data.size()/16 + 1) * _charHeight) + 3); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..c422c58 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,82 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressOffset(int offset); + int addressOffset(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void insert(int i, const QByteArray & ba); + void insert(int i, char ch); + void remove(int index, int len=1); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + +signals: + void currentAddress(int address); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *event); + void setCursorPos(QPoint pos); + void setCursorPos(int position); + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QByteArray _data; + QByteArray _originalData; + QColor _highlightingColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + + bool _blink; + bool _addressArea; + bool _asciiArea; + bool _highlighting; + bool _overwriteMode; + + int _addressNumbers, _realAddressNumbers; + int _addressOffset; + int _charWidth, _charHeight; + int _cursorX, _cursorY, _cursorWidth, _cursorHeight, _cursorPosition; + int _xPosAdr, _xPosHex, _xPosAscii; +}; + +/** \endcond docNever */ + +#endif + diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..0f9d987 --- /dev/null +++ b/ost.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..f53fa81 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..7c7789e --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[PB_HDR_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..e1fbdf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,39 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..2d1bd63 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,291 @@ +/* +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 +*/ + +//#include "pbhelper.h" +#include "rpcserver.h" +#include "pbqtio.h" + +#include + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcServer::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + +_exit: + delete controller; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + delete inStream; + delete outStream; + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcServer::when_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit2; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); + + parsing = false; + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..76f179a --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..cd16732 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,250 @@ +/* +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 +*/ + +#include "abstractport.h" + +#include +#include + +#include "../common/streambase.h" + +AbstractPort::AbstractPort(int id, const char *device) +{ + isUsable_ = true; + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::updatePacketList() +{ + int len; + bool isVariable; + long sec = 0; + long usec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + long numPackets, numBursts; + long ibg = 0, ipg = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->numBursts(); + numPackets = streamList_[i]->burstSize(); + if (streamList_[i]->burstRate() > 0) + ibg = 1000000/streamList_[i]->burstRate(); + break; + case OstProto::StreamControl::e_su_packets: + numBursts = 1; + numPackets = streamList_[i]->numPackets(); + if (streamList_[i]->packetRate() > 0) + ipg = 1000000/streamList_[i]->packetRate(); + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %ld, numPackets = %ld\n", + numBursts, numPackets); + qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + + if (streamList_[i]->isFrameVariable()) + { + isVariable = true; + len = 0; // avoid compiler warning; get len value for each pkt + } + else + { + isVariable = false; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), 0); + } + + for (int j = 0; j < numBursts; j++) + { + for (int k = 0; k < numPackets; k++) + { + if (isVariable) + { + len = streamList_[i]->frameValue(pktBuf_, + sizeof(pktBuf_), j * numPackets + k); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d, %d) sec = %lu usec = %lu", + i, j, k, sec, usec); + + appendToPacketList(sec, usec, pktBuf_, len); + + usec += ipg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numPackets) + + usec += ibg; + if (usec > 1000000) + { + sec++; + usec -= 1000000; + } + } // for (numBursts) + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg : ipg); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; + stats->rxBytes = stats_.rxBytes - epochStats_.rxBytes; + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = stats_.txPkts - epochStats_.txPkts; + stats->txBytes = stats_.txBytes - epochStats_.txBytes; + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..c4f798d --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,111 @@ +/* +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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + bool isUsable() { return isUsable_; } + + virtual void init(); + + int id() { return data_.port_id().id(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, long usecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + bool isUsable_; + OstProto::Port data_; + OstProto::LinkState linkState_; + + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..8206ac2 --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,102 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + trayIcon_->hide(); + + delete trayIcon_; + delete trayIconMenu_; + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..7466a76 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include "ui_drone.h" + +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone +{ + Q_OBJECT + +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..160973b --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,45 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += drone.qrc +HEADERS += drone.h +FORMS += drone.ui +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone.qrc b/server/drone.qrc new file mode 100644 index 0000000..a642656 --- /dev/null +++ b/server/drone.qrc @@ -0,0 +1,5 @@ + + + icons/portgroup.png + + diff --git a/server/drone.ui b/server/drone.ui new file mode 100644 index 0000000..e2e0613 --- /dev/null +++ b/server/drone.ui @@ -0,0 +1,190 @@ + + Drone + + + + 0 + 0 + 268 + 216 + + + + Drone + + + :/icons/portgroup.png + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + (Server) + + + Qt::AlignCenter + + + + + + + TODO: Info/Status here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Exit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show + + + + + Exit + + + + + + + + + pushButton + clicked() + actionExit + trigger() + + + 134 + 194 + + + -1 + -1 + + + + + actionShow + triggered() + Drone + showNormal() + + + -1 + -1 + + + 133 + 107 + + + + + actionExit + triggered() + Drone + close() + + + -1 + -1 + + + 133 + 107 + + + + + diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..3f16bcc --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,49 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +int myport; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Drone drone; + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone.objectName()); + + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone.init()) + exit(-1); + + drone.setWindowFlags(drone.windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone.showMinimized(); + app.exec(); + return 0; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..ebb159a --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,533 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + delete capturer_; + delete transmitter_; + delete monitorTx_; + delete monitorRx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + direction_ = direction; + isDirectional_ = true; + isPromisc_ = true; + stats_ = stats; +_retry: + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), + 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (isPromisc_ && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", device); + isPromisc_ = false; + goto _retry; + } + else + goto _open_error; + } +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); +} + +void PcapPort::PortMonitor::run() +{ + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE] = ""; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for sendQueueList + while(sendQueueList_.size()) + { + pcap_send_queue *sq = sendQueueList_.takeFirst(); + pcap_sendqueue_destroy(sq); + } + setPacketListLoopMode(false, 0); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + pcap_send_queue *sendQ; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = usec; + + sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); + + if ((sendQ == NULL) || + (sendQ->len + sizeof(pcap_pkthdr) + length) > sendQ->maxlen) + { + //! \todo (LOW): calculate sendqueue size + sendQ = pcap_sendqueue_alloc(1*1024*1024); + sendQueueList_.append(sendQ); + + // Validate that the pkt will fit inside the new sendQ + Q_ASSERT((length + sizeof(pcap_pkthdr)) < sendQ->maxlen); + } + + if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) + op = false; + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + + qDebug("sendQueueList_.size = %d", sendQueueList_.size()); + if (sendQueueList_.size() <= 0) + return; + + for(i = 0; i < sendQueueList_.size(); i++) + { + int ret; +_restart: + ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); + + if (ret < 0) + { + qDebug("error %d in sendQueueTransmit()", ret); + stop_ = false; + return; + } + } + + if (returnToQIdx_ >= 0) + { + i = returnToQIdx_; + + udelay(loopDelay_); + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, int sync) +{ + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + while (1) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (stop_) + { + return -2; + } + + // A pktLen of size 0 is used at the end of a sendQueue and before + // the next sendQueue - i.e. for inter sendQueue timing + if(pktLen > 0) + { + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + } + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + // Check if the end of the user buffer has been reached + if((char*) hdr >= end) + return 0; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + if (usec) + { + udelay(usec); + ts = hdr->ts; + } + } + } +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#ifdef Q_OS_WIN32 + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } +_retry: + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + flag, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..e82589b --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0); + } + virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, usec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, long usecDelay) { + transmitter_->setPacketListLoopMode(loop, usecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } + protected: + AbstractPort::PortStats *stats_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + bool isPromisc_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, long usecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = usecDelay; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + + quint64 ticksFreq_; + QList sendQueueList_; + int returnToQIdx_; + long loopDelay_; + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..081d5db --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,86 @@ +/* +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 +*/ + +#include "portmanager.h" + +#include +#include + +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + +#ifdef Q_OS_WIN32 + port = new WinPcapPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + + port->init(); + portList_.append(port); + } + + pcap_freealldevs(deviceList); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..6cdbce8 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,216 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + if (handle()) + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..d5e8af1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,109 @@ + +#include "ostprotolib.h" +#include "pcapfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" +#include "settings.h" + +#include +#include +#include +#include + +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int /*argc*/, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) +{ + bool isOk; + QString error; + + if (argc != 3) + { + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; + } + + OstProto::StreamConfigList streams; + QString inFile(argv[2]); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) + { + printf("%s: %s\n", + inFile.toAscii().constData(), error.toAscii().constData()); + } + + if (!isOk) + return 1; + + return 0; +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..8dbe0cb --- /dev/null +++ b/test/test.pro @@ -0,0 +1,34 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" "../client" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 + +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..7422c3c --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.3 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From 9bd5bcb736d8f41b3c33e646b373ee19e0fe89c6 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 17 Apr 2011 10:57:39 +0530 Subject: [PATCH 166/294] Bumped version to 0.4 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index 7422c3c..b716601 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.3 +APP_VERSION = 0.4 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 1adf52033cc8877cdee97c03636645968b4e9820 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 8 May 2011 13:04:14 +0530 Subject: [PATCH 167/294] On some linux distros, the native "save" dialog can't distinguish the Ostinato and PCAP filters - so we don't use native dialog for *nix platforms --- client/portswindow.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 837fb89..f6817e1 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -521,8 +521,9 @@ void PortsWindow::on_actionSave_Streams_triggered() QFileDialog::Options options; // On Mac OS with Native Dialog, getSaveFileName() ignores fileType - // which we need. -#ifdef Q_OS_MAC + // which we need.On some Linux distros the native dialog can't + // distinguish between Ostinato(*) and PCAP(*) +#if defined(Q_OS_MAC) || defined(Q_OS_UNIX) options |= QFileDialog::DontUseNativeDialog; #endif From 8d55f68c617316b39c1384de30ce303e66055c3d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 8 May 2011 16:56:15 +0530 Subject: [PATCH 168/294] Added explicit option to generate the "generic service code" since by default protobuf 2.4.0 and later don't generate them. Also since protobuf 2.3.0 is the first version that understands this option, the minimum protobuf version required for Ostinato now becomes 2.3.0 --- common/protocol.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/protocol.proto b/common/protocol.proto index 5e3f208..1c9aaa3 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -18,6 +18,7 @@ along with this program. If not, see */ package OstProto; +option cc_generic_services = true; message StreamId { required uint32 id = 1; From 0ffd49a428e03b75b8f5227fb4e32e335bc8879d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 8 May 2011 17:58:28 +0530 Subject: [PATCH 169/294] Bumped version to 0.4.1 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index b716601..ed0d8da 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.4 +APP_VERSION = 0.4.1 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 955be7108236b0d4788dfc2c26fe1d2070de2f94 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 19 Jun 2011 16:26:35 +0530 Subject: [PATCH 170/294] Changes to improve pps performance for Linux. Changed implementation of stats collection for Linux - pps/Bps stats are now supported Fixes Issue 26 --- server/abstractport.h | 1 + server/drone.pro | 1 + server/linuxport.cpp | 275 +++++++++++++++++++++++++++++++++++++++++ server/linuxport.h | 56 +++++++++ server/pcapport.cpp | 96 +++++++++++--- server/pcapport.h | 1 + server/portmanager.cpp | 9 +- 7 files changed, 417 insertions(+), 22 deletions(-) create mode 100644 server/linuxport.cpp create mode 100644 server/linuxport.h diff --git a/server/abstractport.h b/server/abstractport.h index c4f798d..954014f 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -52,6 +52,7 @@ public: virtual void init(); int id() { return data_.port_id().id(); } + const char* name() { return data_.name().c_str(); } void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } bool modify(const OstProto::Port &port); diff --git a/server/drone.pro b/server/drone.pro index 160973b..a317414 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -35,6 +35,7 @@ SOURCES += \ portmanager.cpp \ abstractport.cpp \ pcapport.cpp \ + linuxport.cpp \ winpcapport.cpp SOURCES += myservice.cpp SOURCES += pcapextra.cpp diff --git a/server/linuxport.cpp b/server/linuxport.cpp new file mode 100644 index 0000000..8d52cc9 --- /dev/null +++ b/server/linuxport.cpp @@ -0,0 +1,275 @@ +/* +Copyright (C) 2011 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 "linuxport.h" + +#ifdef Q_OS_LINUX + +#include + +#include +#include +#include + +QList LinuxPort::allPorts_; +LinuxPort::StatsMonitor *LinuxPort::monitor_; + +LinuxPort::LinuxPort(int id, const char *device) + : PcapPort(id, device) +{ + // We don't need per port Rx/Tx monitors for Linux + delete monitorRx_; + delete monitorTx_; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); +} + +LinuxPort::~LinuxPort() +{ +} + +void LinuxPort::init() +{ + // TODO: Update Notes with Promisc/Non-Promisc + + if (!monitor_->isRunning()) + monitor_->start(); +} + +OstProto::LinkState LinuxPort::linkState() +{ + // TODO + return linkState_; +} + +bool LinuxPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool LinuxPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +LinuxPort::StatsMonitor::StatsMonitor() + : QThread() +{ +} + +void LinuxPort::StatsMonitor::run() +{ + PortStats **portStats; + int fd; + QByteArray buf; + int len; + char *p, *end; + int count, index; + const char* fmtopt[] = { + "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u%n\n", + }; + const char *fmt; + + // + // We first setup stuff before we start polling for stats + // + fd = open("/proc/net/dev", O_RDONLY); + if (fd < 0) + { + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; + } + + buf.fill('\0', 8192); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + qWarning("initial buffer size is too small. no stats will be available"); + return; + } + + p = buf.data(); + end = p + len; + + // Select scanf format + if (strstr(buf, "compressed")) + fmt = fmtopt[0]; + else + fmt = fmtopt[1]; + + // Count number of lines - number of ports is 2 less than number of lines + count = 0; + while (p < end) + { + if (*p == '\n') + count++; + p++; + } + count -= 2; + + if (count <= 0) + { + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; + } + + portStats = (PortStats**) calloc(count, sizeof(PortStats)); + Q_ASSERT(portStats != NULL); + + // + // Populate the port stats array + // + p = buf.data(); + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + char* q; + + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; + + q = p; + + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) + { + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; + p++; + } + Q_ASSERT(index == count); + + qDebug("stats for %d ports setup", count); + + // + // We are all set - Let's start polling for stats! + // + while (1) + { + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; + } + + p = buf.data(); + end = p + len; + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + stats->rxPps = (rxPkts - stats->rxPkts)/kRefreshFreq_; + stats->rxBps = (rxBytes - stats->rxBytes)/kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = (txPkts - stats->txPkts)/kRefreshFreq_; + stats->txBps = (txBytes - stats->txBytes)/kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); + } +} + +#endif diff --git a/server/linuxport.h b/server/linuxport.h new file mode 100644 index 0000000..8fbedf5 --- /dev/null +++ b/server/linuxport.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SERVER_LINUX_PORT_H +#define _SERVER_LINUX_PORT_H + +#include + +#ifdef Q_OS_LINUX + +#include "pcapport.h" + +class LinuxPort : public PcapPort +{ +public: + LinuxPort(int id, const char *device); + ~LinuxPort(); + + void init(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + private: + static const int kRefreshFreq_ = 1; // in seconds + }; + + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/pcapport.cpp b/server/pcapport.cpp index ebb159a..0777543 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -27,6 +27,34 @@ along with this program. If not, see pcap_if_t *PcapPort::deviceList_ = NULL; + +#if defined(Q_OS_LINUX) +typedef struct timeval TimeStamp; +static void inline getTimeStamp(TimeStamp *stamp) +{ + gettimeofday(stamp, NULL); +} + +// Returns time diff in usecs between end and start +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + struct timeval diff; + long usecs; + + timersub(end, start, &diff); + + usecs = diff.tv_usec; + if (diff.tv_sec) + usecs += diff.tv_sec*1e6; + + return usecs; +} +#else +typedef int TimeStamp; +static void inline getTimeStamp(TimeStamp*) {} +static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } +#endif + PcapPort::PcapPort(int id, const char *device) : AbstractPort(id, device) { @@ -169,6 +197,11 @@ _open_error: qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); } +PcapPort::PortMonitor::~PortMonitor() +{ + pcap_close(handle_); +} + void PcapPort::PortMonitor::run() { while (1) @@ -279,8 +312,15 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); if ((sendQ == NULL) || - (sendQ->len + sizeof(pcap_pkthdr) + length) > sendQ->maxlen) + (sendQ->len + 2*sizeof(pcap_pkthdr) + length) > sendQ->maxlen) { + // Add a zero len packet at end of sendQ for inter-sendQ timing + if (sendQ) + { + pcap_pkthdr hdr = pktHdr; + hdr.caplen = 0; + pcap_sendqueue_queue(sendQ, &hdr, (u_char*)packet); + } //! \todo (LOW): calculate sendqueue size sendQ = pcap_sendqueue_alloc(1*1024*1024); sendQueueList_.append(sendQ); @@ -352,7 +392,8 @@ _restart: { i = returnToQIdx_; - udelay(loopDelay_); + if (loopDelay_) + udelay(loopDelay_); goto _restart; } } @@ -366,20 +407,32 @@ void PcapPort::PortTransmitter::stop() int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync) { + TimeStamp ovrStart, ovrEnd; struct timeval ts; struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; char *end = queue->buffer + queue->len; ts = hdr->ts; - while (1) + getTimeStamp(&ovrStart); + while((char*) hdr < end) { uchar *pkt = (uchar*)hdr + sizeof(*hdr); int pktLen = hdr->caplen; - if (stop_) + if (sync) { - return -2; + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + getTimeStamp(&ovrEnd); + + usec -= udiffTimeStamp(&ovrStart, &ovrEnd); + if (usec > 0) + udelay(usec); + + ts = hdr->ts; + getTimeStamp(&ovrStart); } // A pktLen of size 0 is used at the end of a sendQueue and before @@ -392,30 +445,21 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, } // Step to the next packet in the buffer - hdr = (struct pcap_pkthdr*) ((uchar*)hdr + sizeof(*hdr) + pktLen); + hdr = (struct pcap_pkthdr*) (pkt + pktLen); pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); - // Check if the end of the user buffer has been reached - if((char*) hdr >= end) - return 0; - - if (sync) + if (stop_) { - long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + - (hdr->ts.tv_usec - ts.tv_usec); - - if (usec) - { - udelay(usec); - ts = hdr->ts; - } + return -2; } } + + return 0; } void PcapPort::PortTransmitter::udelay(long usec) { -#ifdef Q_OS_WIN32 +#if defined(Q_OS_WIN32) LARGE_INTEGER tgtTicks; LARGE_INTEGER curTicks; @@ -424,6 +468,18 @@ void PcapPort::PortTransmitter::udelay(long usec) while (curTicks.QuadPart < tgtTicks.QuadPart) QueryPerformanceCounter(&curTicks); +#elif defined(Q_OS_LINUX) + struct timeval delay, target, now; + + delay.tv_sec = 0; + delay.tv_usec = usec; + + gettimeofday(&now, NULL); + timeradd(&now, &delay, &target); + + do { + gettimeofday(&now, NULL); + } while (timercmp(&now, &target, <)); #else QThread::usleep(usec); #endif diff --git a/server/pcapport.h b/server/pcapport.h index e82589b..ab5b24a 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -74,6 +74,7 @@ protected: public: PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats); + ~PortMonitor(); void run(); pcap_t* handle() { return handle_; } Direction direction() { return direction_; } diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 081d5db..9a47ae7 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -22,6 +22,7 @@ along with this program. If not, see #include #include +#include "linuxport.h" #include "pcapport.h" #include "winpcapport.h" @@ -47,8 +48,10 @@ PortManager::PortManager() if (device->description) qDebug(" (%s)\n", device->description); -#ifdef Q_OS_WIN32 +#if defined(Q_OS_WIN32) port = new WinPcapPort(i, device->name); +#elif defined(Q_OS_LINUX) + port = new LinuxPort(i, device->name); #else port = new PcapPort(i, device->name); #endif @@ -62,11 +65,13 @@ PortManager::PortManager() continue; } - port->init(); portList_.append(port); } pcap_freealldevs(deviceList); + + foreach(AbstractPort *port, portList_) + port->init(); return; } From aae32eb510cb8fa2200e72b1e9d4af5452abed22 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 26 Jun 2011 10:33:06 +0530 Subject: [PATCH 171/294] Fixed burst mode timing problem by taking into account overhead --- server/pcapport.cpp | 44 +++++++++++++++++++++++++++++++------------- server/pcapport.h | 3 ++- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 0777543..7aa63b5 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -314,13 +314,13 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, if ((sendQ == NULL) || (sendQ->len + 2*sizeof(pcap_pkthdr) + length) > sendQ->maxlen) { - // Add a zero len packet at end of sendQ for inter-sendQ timing - if (sendQ) - { - pcap_pkthdr hdr = pktHdr; - hdr.caplen = 0; - pcap_sendqueue_queue(sendQ, &hdr, (u_char*)packet); - } + // Add a zero len packet at end of sendQ for inter-sendQ timing + if (sendQ) + { + pcap_pkthdr hdr = pktHdr; + hdr.caplen = 0; + pcap_sendqueue_queue(sendQ, &hdr, (u_char*)packet); + } //! \todo (LOW): calculate sendqueue size sendQ = pcap_sendqueue_alloc(1*1024*1024); sendQueueList_.append(sendQ); @@ -369,6 +369,7 @@ void PcapPort::PortTransmitter::run() const int kSyncTransmit = 1; int i; + long overHead = 0; qDebug("sendQueueList_.size = %d", sendQueueList_.size()); if (sendQueueList_.size() <= 0) @@ -378,11 +379,13 @@ void PcapPort::PortTransmitter::run() { int ret; _restart: - ret = sendQueueTransmit(handle_, sendQueueList_.at(i), kSyncTransmit); + ret = sendQueueTransmit(handle_, sendQueueList_.at(i), overHead, + kSyncTransmit); if (ret < 0) { qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); stop_ = false; return; } @@ -390,10 +393,17 @@ _restart: if (returnToQIdx_ >= 0) { - i = returnToQIdx_; + long usecs = loopDelay_ + overHead; - if (loopDelay_) - udelay(loopDelay_); + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + + i = returnToQIdx_; goto _restart; } } @@ -405,7 +415,7 @@ void PcapPort::PortTransmitter::stop() } int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, - pcap_send_queue *queue, int sync) + pcap_send_queue *queue, long &overHead, int sync) { TimeStamp ovrStart, ovrEnd; struct timeval ts; @@ -427,9 +437,15 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, getTimeStamp(&ovrEnd); - usec -= udiffTimeStamp(&ovrStart, &ovrEnd); + overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + usec += overHead; if (usec > 0) + { udelay(usec); + overHead = 0; + } + else + overHead = usec; ts = hdr->ts; getTimeStamp(&ovrStart); @@ -471,6 +487,8 @@ void PcapPort::PortTransmitter::udelay(long usec) #elif defined(Q_OS_LINUX) struct timeval delay, target, now; + //qDebug("usec delay = %ld", usec); + delay.tv_sec = 0; delay.tv_usec = usec; diff --git a/server/pcapport.h b/server/pcapport.h index ab5b24a..f5acfda 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -107,7 +107,8 @@ protected: void stop(); private: void udelay(long usec); - int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, + int sync); quint64 ticksFreq_; QList sendQueueList_; From d2c4bf0834a9108397f594db144de200d0f9077b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 7 Jul 2011 21:43:12 +0530 Subject: [PATCH 172/294] Fixed packet rate accuracy when inter-packet (or inter-burst) gap had resolution less than a millisecond. --- server/abstractport.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index cd16732..028ca15 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -23,6 +23,7 @@ along with this program. If not, see #include #include "../common/streambase.h" +#include AbstractPort::AbstractPort(int id, const char *device) { @@ -129,7 +130,12 @@ void AbstractPort::updatePacketList() if (streamList_[i]->isEnabled()) { long numPackets, numBursts; - long ibg = 0, ipg = 0; + double ibg = 0; + long ibg1 = 0, ibg2 = 0; + long nb1 = 0, nb2 = 0; + double ipg = 0; + long ipg1 = 0, ipg2 = 0; + long np1 = 0, np2 = 0; switch (streamList_[i]->sendUnit()) { @@ -137,13 +143,25 @@ void AbstractPort::updatePacketList() numBursts = streamList_[i]->numBursts(); numPackets = streamList_[i]->burstSize(); if (streamList_[i]->burstRate() > 0) - ibg = 1000000/streamList_[i]->burstRate(); + { + ibg = 1e6/double(streamList_[i]->burstRate()); + ibg1 = ceil(ibg); + ibg2 = floor(ibg); + nb1 = long((ibg - double(ibg2)) * double(numBursts)); + nb2= numBursts - nb1; + } break; case OstProto::StreamControl::e_su_packets: numBursts = 1; numPackets = streamList_[i]->numPackets(); if (streamList_[i]->packetRate() > 0) - ipg = 1000000/streamList_[i]->packetRate(); + { + ipg = 1e6/double(streamList_[i]->packetRate()); + ipg1 = ceil(ipg); + ipg2 = floor(ipg); + np1 = long((ipg - double(ipg2)) * double(numPackets)); + np2= numPackets - np1; + } break; default: qWarning("Unhandled stream control unit %d", @@ -152,7 +170,10 @@ void AbstractPort::updatePacketList() } qDebug("numBursts = %ld, numPackets = %ld\n", numBursts, numPackets); - qDebug("ibg = %ld, ipg = %ld\n", ibg, ipg); + qDebug("ibg = %g, ibg1/nb1 = %ld/%ld ibg2/nb2 = %ld/%ld\n", + ibg, ibg1, nb1, ibg2, nb2); + qDebug("ipg = %g, ipg1/np1 = %ld/%ld ipg2/np2 = %ld/%ld\n", + ipg, ipg1, np1, ipg2, np2); if (streamList_[i]->isFrameVariable()) { @@ -182,7 +203,7 @@ void AbstractPort::updatePacketList() appendToPacketList(sec, usec, pktBuf_, len); - usec += ipg; + usec += (k < np1) ? ipg1 : ipg2; if (usec > 1000000) { sec++; @@ -190,7 +211,7 @@ void AbstractPort::updatePacketList() } } // for (numPackets) - usec += ibg; + usec += (j < nb1) ? ibg1 : ibg2; if (usec > 1000000) { sec++; @@ -217,7 +238,7 @@ void AbstractPort::updatePacketList() */ setPacketListLoopMode(true, streamList_[i]->sendUnit() == - StreamBase::e_su_bursts ? ibg : ipg); + StreamBase::e_su_bursts ? ibg1 : ipg1); goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_next: From 670920afa6d27d01c448926d36f1f1a2ec7044b4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 9 Jul 2011 20:40:42 +0530 Subject: [PATCH 173/294] Enable inter-packet gap to be more than one second Fixes issue 42 --- client/streamconfigdialog.cpp | 4 ++-- common/fileformat.cpp | 39 ++++++++++++++++++++++++++++++++++- common/fileformat.h | 4 +++- common/protocol.proto | 6 ++++-- common/streambase.cpp | 12 +++++------ common/streambase.h | 8 +++---- server/abstractport.cpp | 4 ++-- server/pcapport.cpp | 6 ++++++ 8 files changed, 65 insertions(+), 18 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index cb7579b..c98cd52 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -964,8 +964,8 @@ void StreamConfigDialog::StoreCurrentStream() pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); - pStream->setPacketRate(lePacketsPerSec->text().toULong(&isOk)); - pStream->setBurstRate(leBurstsPerSec->text().toULong(&isOk)); + pStream->setPacketRate(lePacketsPerSec->text().toDouble(&isOk)); + pStream->setBurstRate(leBurstsPerSec->text().toDouble(&isOk)); } } diff --git a/common/fileformat.cpp b/common/fileformat.cpp index aada8b1..4edd980 100644 --- a/common/fileformat.cpp +++ b/common/fileformat.cpp @@ -161,7 +161,6 @@ bool FileFormat::openStreams(const QString fileName, } Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); - Q_ASSERT(meta.data().format_version_minor() == kFileFormatVersionMinor); // ByteSize() does not include the Tag/Key, so we add 2 for that contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; @@ -178,6 +177,8 @@ bool FileFormat::openStreams(const QString fileName, if (!content.matter().has_streams()) goto _missing_streams; + postParseFixup(meta.data(), content); + streams.CopyFrom(content.matter().streams()); return true; @@ -444,3 +445,39 @@ void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) qApp->property("revision").toString().toUtf8().constData()); } +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void FileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/fileformat.h b/common/fileformat.h index 0204906..409b8a8 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -39,6 +39,8 @@ public: private: void initFileMetaData(OstProto::FileMetaData &metaData); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); static const int kFileMagicSize = 12; static const int kFileChecksumSize = 5; @@ -51,7 +53,7 @@ private: // Native file format version static const uint kFileFormatVersionMajor = 0; - static const uint kFileFormatVersionMinor = 1; + static const uint kFileFormatVersionMinor = 2; static const uint kFileFormatVersionRevision = 3; }; diff --git a/common/protocol.proto b/common/protocol.proto index 1c9aaa3..9ac1c9a 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -67,8 +67,10 @@ message StreamControl { optional uint32 num_bursts = 4 [default = 1]; optional uint32 packets_per_burst = 5 [default = 10]; optional NextWhat next = 6 [default = e_nw_goto_next]; - optional uint32 packets_per_sec = 7 [default = 1]; - optional uint32 bursts_per_sec = 8 [default = 1]; + optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; + optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; + optional double packets_per_sec = 9 [default = 1]; + optional double bursts_per_sec = 10 [default = 1]; } message ProtocolId { diff --git a/common/streambase.cpp b/common/streambase.cpp index b9dea25..720b801 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -325,23 +325,23 @@ bool StreamBase::setBurstSize(quint32 packetsPerBurst) return true; } -quint32 StreamBase::packetRate() const +double StreamBase::packetRate() const { - return (quint32) mControl->packets_per_sec(); + return (double) mControl->packets_per_sec(); } -bool StreamBase::setPacketRate(quint32 packetsPerSec) +bool StreamBase::setPacketRate(double packetsPerSec) { mControl->set_packets_per_sec(packetsPerSec); return true; } -quint32 StreamBase::burstRate() const +double StreamBase::burstRate() const { - return (quint32) mControl->bursts_per_sec(); + return (double) mControl->bursts_per_sec(); } -bool StreamBase::setBurstRate(quint32 burstsPerSec) +bool StreamBase::setBurstRate(double burstsPerSec) { mControl->set_bursts_per_sec(burstsPerSec); return true; diff --git a/common/streambase.h b/common/streambase.h index 7afbbd9..0d1e53b 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -125,11 +125,11 @@ public: quint32 burstSize() const; bool setBurstSize(quint32 packetsPerBurst); - quint32 packetRate() const; - bool setPacketRate(quint32 packetsPerSec); + double packetRate() const; + bool setPacketRate(double packetsPerSec); - quint32 burstRate() const; - bool setBurstRate(quint32 burstsPerSec); + double burstRate() const; + bool setBurstRate(double burstsPerSec); bool isFrameVariable() const; bool isFrameSizeVariable() const; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 028ca15..252f4b5 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -204,7 +204,7 @@ void AbstractPort::updatePacketList() appendToPacketList(sec, usec, pktBuf_, len); usec += (k < np1) ? ipg1 : ipg2; - if (usec > 1000000) + while (usec >= 1000000) { sec++; usec -= 1000000; @@ -212,7 +212,7 @@ void AbstractPort::updatePacketList() } // for (numPackets) usec += (j < nb1) ? ibg1 : ibg2; - if (usec > 1000000) + while (usec >= 1000000) { sec++; usec -= 1000000; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 7aa63b5..c92eae8 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -492,6 +492,12 @@ void PcapPort::PortTransmitter::udelay(long usec) delay.tv_sec = 0; delay.tv_usec = usec; + while (delay.tv_usec >= 1000000) + { + delay.tv_sec++; + delay.tv_usec -= 1000000; + } + gettimeofday(&now, NULL); timeradd(&now, &delay, &target); From b3bc9e36a7d79c3f65dbb69df85d423f92e7a82b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 18 Jul 2011 19:22:32 +0530 Subject: [PATCH 174/294] Inter-packet and Inter-burst gaps are now calculated in nanosecond rather than milliseconds. However, the actual delay is still in milliseconds for all OS as of now. Fixes issue 30 --- server/abstractport.cpp | 24 ++++++++++++------------ server/abstractport.h | 4 ++-- server/pcapport.cpp | 4 ++-- server/pcapport.h | 12 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 252f4b5..193a480 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -116,7 +116,7 @@ void AbstractPort::updatePacketList() int len; bool isVariable; long sec = 0; - long usec = 0; + long nsec = 0; qDebug("In %s", __FUNCTION__); @@ -144,7 +144,7 @@ void AbstractPort::updatePacketList() numPackets = streamList_[i]->burstSize(); if (streamList_[i]->burstRate() > 0) { - ibg = 1e6/double(streamList_[i]->burstRate()); + ibg = 1e9/double(streamList_[i]->burstRate()); ibg1 = ceil(ibg); ibg2 = floor(ibg); nb1 = long((ibg - double(ibg2)) * double(numBursts)); @@ -156,7 +156,7 @@ void AbstractPort::updatePacketList() numPackets = streamList_[i]->numPackets(); if (streamList_[i]->packetRate() > 0) { - ipg = 1e6/double(streamList_[i]->packetRate()); + ipg = 1e9/double(streamList_[i]->packetRate()); ipg1 = ceil(ipg); ipg2 = floor(ipg); np1 = long((ipg - double(ipg2)) * double(numPackets)); @@ -198,24 +198,24 @@ void AbstractPort::updatePacketList() if (len <= 0) continue; - qDebug("q(%d, %d, %d) sec = %lu usec = %lu", - i, j, k, sec, usec); + qDebug("q(%d, %d, %d) sec = %lu nsec = %lu", + i, j, k, sec, nsec); - appendToPacketList(sec, usec, pktBuf_, len); + appendToPacketList(sec, nsec, pktBuf_, len); - usec += (k < np1) ? ipg1 : ipg2; - while (usec >= 1000000) + nsec += (k < np1) ? ipg1 : ipg2; + while (nsec >= 1e9) { sec++; - usec -= 1000000; + nsec -= 1e9; } } // for (numPackets) - usec += (j < nb1) ? ibg1 : ibg2; - while (usec >= 1000000) + nsec += (j < nb1) ? ibg1 : ibg2; + while (nsec >= 1e9) { sec++; - usec -= 1000000; + nsec -= 1e9; } } // for (numBursts) diff --git a/server/abstractport.h b/server/abstractport.h index 954014f..18d0b1e 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -71,9 +71,9 @@ public: void setDirty() { isSendQueueDirty_ = true; } virtual void clearPacketList() = 0; - virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; - virtual void setPacketListLoopMode(bool loop, long usecDelay) = 0; + virtual void setPacketListLoopMode(bool loop, long nsecDelay) = 0; void updatePacketList(); virtual void startTransmit() = 0; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index c92eae8..fba37e1 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -298,7 +298,7 @@ void PcapPort::PortTransmitter::clearPacketList() setPacketListLoopMode(false, 0); } -bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, const uchar *packet, int length) { bool op = true; @@ -307,7 +307,7 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long usec, pktHdr.caplen = pktHdr.len = length; pktHdr.ts.tv_sec = sec; - pktHdr.ts.tv_usec = usec; + pktHdr.ts.tv_usec = nsec/1000; sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); diff --git a/server/pcapport.h b/server/pcapport.h index f5acfda..53e5867 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -42,12 +42,12 @@ public: transmitter_->clearPacketList(); setPacketListLoopMode(false, 0); } - virtual bool appendToPacketList(long sec, long usec, const uchar *packet, + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) { - return transmitter_->appendToPacketList(sec, usec, packet, length); + return transmitter_->appendToPacketList(sec, nsec, packet, length); } - virtual void setPacketListLoopMode(bool loop, long usecDelay) { - transmitter_->setPacketListLoopMode(loop, usecDelay); + virtual void setPacketListLoopMode(bool loop, long nsecDelay) { + transmitter_->setPacketListLoopMode(loop, nsecDelay); } virtual void startTransmit() { @@ -97,9 +97,9 @@ protected: void clearPacketList(); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); - void setPacketListLoopMode(bool loop, long usecDelay) { + void setPacketListLoopMode(bool loop, long nsecDelay) { returnToQIdx_ = loop ? 0 : -1; - loopDelay_ = usecDelay; + loopDelay_ = nsecDelay/1000; } void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); From ca4a6cb103489a72ddeb709620681e8ab288c250 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 14 Aug 2011 14:03:57 +0530 Subject: [PATCH 175/294] Implemented aggregate port rates, averaged across all active streams configured on the port. Fixes issue 41 --- client/ostinato.qrc | 1 + client/port.cpp | 112 ++++++++++++ client/port.h | 14 ++ client/portswindow.cpp | 74 +++++++- client/portswindow.h | 5 + client/portswindow.ui | 198 ++++++++++++-------- client/streamconfigdialog.cpp | 122 ++++++++++++- client/streamconfigdialog.h | 9 + client/streamconfigdialog.ui | 331 +++++++++++++++++++++------------- common/streambase.cpp | 51 ++++++ common/streambase.h | 5 + server/abstractport.cpp | 12 +- 12 files changed, 711 insertions(+), 223 deletions(-) diff --git a/client/ostinato.qrc b/client/ostinato.qrc index bd88fd1..6162ac0 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -16,6 +16,7 @@ icons/deco_exclusive.png icons/delete.png icons/exit.png + icons/gaps.png icons/logo.png icons/magnifier.png icons/name.png diff --git a/client/port.cpp b/client/port.cpp index 1acb94d..79bc8b5 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -69,6 +69,115 @@ void Port::reorderStreamsByOrdinals() qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); } +void Port::recalculateAverageRates() +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + double r = s->averagePacketRate(); + pps += r; + bps += r * s->frameLenAvg() * 8; + n++; + + if (s->nextWhat() == Stream::e_nw_stop) + break; + } + + if (n) + { + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAveragePacketRate(double packetsPerSec) +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + s->setAveragePacketRate(packetsPerSec); + + double r = s->averagePacketRate(); + pps += r; + bps += r * s->frameLenAvg() * 8; + n++; + + if (s->nextWhat() == Stream::e_nw_stop) + break; + } + + if (n) + { + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAverageBitRate(double bitsPerSec) +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + s->setAveragePacketRate(bitsPerSec/s->frameLenAvg()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * s->frameLenAvg() * 8; + n++; + + if (s->nextWhat() == Stream::e_nw_stop) + break; + } + + if (n) + { + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + bool Port::newStreamAt(int index, OstProto::Stream const *stream) { Stream *s = new Stream; @@ -82,6 +191,7 @@ bool Port::newStreamAt(int index, OstProto::Stream const *stream) s->setId(newStreamId()); mStreams.insert(index, s); updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); return true; } @@ -93,6 +203,7 @@ bool Port::deleteStreamAt(int index) delete mStreams.takeAt(index); updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); return true; } @@ -312,6 +423,7 @@ _user_opt_cancel: _fail: progress.close(); mainWindow->setEnabled(true); + recalculateAverageRates(); return ret; } diff --git a/client/port.h b/client/port.h index 275ea61..52cd72a 100644 --- a/client/port.h +++ b/client/port.h @@ -43,6 +43,9 @@ class Port : public QObject { quint32 mPortGroupId; QString mUserAlias; // user defined + double avgPacketsPerSec_; + double avgBitsPerSec_; + QList mLastSyncStreamList; QList mStreams; // sorted by stream's ordinal value @@ -50,6 +53,7 @@ class Port : public QObject { void updateStreamOrdinalsFromIndex(); void reorderStreamsByOrdinals(); + public: enum AdminStatus { AdminDisable, AdminEnable }; @@ -72,6 +76,10 @@ public: { return (d.is_enabled()?AdminEnable:AdminDisable); } bool hasExclusiveControl() { return d.is_exclusive_control(); } + double averagePacketRate() + { return avgPacketsPerSec_; } + double averageBitRate() + { return avgBitsPerSec_; } //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString &alias) { mUserAlias = alias; } @@ -116,12 +124,18 @@ public: void when_syncComplete(); + void setAveragePacketRate(double packetsPerSec); + void setAverageBitRate(double bitsPerSec); + // FIXME(MED): Bad Hack! port should not need an external trigger to + // recalculate - refactor client side domain objects and model objects + void recalculateAverageRates(); void updateStats(OstProto::PortStats *portStats); bool openStreams(QString fileName, bool append, QString &error); bool saveStreams(QString fileName, QString fileType, QString &error); signals: + void portRateChanged(int portGroupId, int portId); void portDataChanged(int portGroupId, int portId); void streamListChanged(int portGroupId, int portId); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index f6817e1..5ed280a 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -109,8 +109,18 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) when_portView_currentChanged(QModelIndex(), QModelIndex()); updateStreamViewActions(); - //! \todo Hide the Aggregate Box till we add support - frAggregate->setHidden(true); + connect(plm->getStreamModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(streamModelDataChanged())); + connect(plm->getStreamModel(), + SIGNAL(modelReset()), + this, SLOT(streamModelDataChanged())); +} + +void PortsWindow::streamModelDataChanged() +{ + if (plm->isPort(tvPortList->currentIndex())) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); } PortsWindow::~PortsWindow() @@ -121,6 +131,7 @@ PortsWindow::~PortsWindow() void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) { StreamConfigDialog *scd; + int ret; if (!index.isValid()) { @@ -130,17 +141,27 @@ void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), index.row(), this); qDebug("stream list activated\n"); - scd->exec(); // TODO: chk retval + ret = scd->exec(); + + if (ret == QDialog::Accepted) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); + delete scd; } void PortsWindow::when_portView_currentChanged(const QModelIndex& current, - const QModelIndex& /*previous*/) + const QModelIndex& previous) { plm->getStreamModel()->setCurrentPortIndex(current); updatePortViewActions(current); updateStreamViewActions(); + if (previous.isValid() && plm->isPort(previous)) + { + disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), + this, SLOT(updatePortRates())); + } + if (!current.isValid()) { qDebug("setting stacked widget to blank page"); @@ -154,7 +175,10 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& current, } else if (plm->isPort(current)) { - swDetail->setCurrentIndex(0); // port detail page + swDetail->setCurrentIndex(0); // port detail page + updatePortRates(); + connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), + SLOT(updatePortRates())); } } } @@ -180,6 +204,46 @@ void PortsWindow::when_portModel_reset() when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } +void PortsWindow::on_averagePacketsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); + + plm->port(current).setAveragePacketRate(pps); +} + +void PortsWindow::on_averageBitsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk)/8; + + plm->port(current).setAverageBitRate(bps); +} + +void PortsWindow::updatePortRates() +{ + QModelIndex current = tvPortList->currentIndex(); + + if (!current.isValid()) + return; + + if (!plm->isPort(current)) + return; + + averagePacketsPerSec->setText(QString("%L1") + .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); + averageBitsPerSec->setText(QString("%L1") + .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); +} + void PortsWindow::updateStreamViewActions() { // For some reason hasSelection() returns true even if selection size is 0 diff --git a/client/portswindow.h b/client/portswindow.h index ed93854..3921296 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -52,6 +52,9 @@ private slots: void updatePortViewActions(const QModelIndex& current); void updateStreamViewActions(); + void on_averagePacketsPerSec_editingFinished(); + void on_averageBitsPerSec_editingFinished(); + void updatePortRates(); void on_tvStreamList_activated(const QModelIndex & index); void when_portView_currentChanged(const QModelIndex& current, const QModelIndex& previous); @@ -74,6 +77,8 @@ private slots: void on_actionOpen_Streams_triggered(); void on_actionSave_Streams_triggered(); + + void streamModelDataChanged(); }; #endif diff --git a/client/portswindow.ui b/client/portswindow.ui index a724c06..1441012 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -5,15 +5,15 @@ 0 0 - 689 + 710 352 Form - - + + Qt::Horizontal @@ -34,10 +34,7 @@ 0 - - - 6 - + 0 @@ -50,84 +47,100 @@ 0 - + - - - Qt::Horizontal + + + + 0 + 0 + - - - 40 - 20 - + + QFrame::StyledPanel - + + QFrame::Sunken + + + + + + Avg pps + + + true + + + + + + + + + + Avg bps + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - - - Apply - - + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Capacity - - - - - - - - - - Aggr fps - - - - - - - - - - % age - - - - - - - - - - Aggr bps - - - - - - - - - - + + + + 0 + 1 + + Qt::ActionsContextMenu @@ -244,5 +257,38 @@ - + + + radioButton + toggled(bool) + averagePacketsPerSec + setEnabled(bool) + + + 313 + 28 + + + 380 + 28 + + + + + radioButton_2 + toggled(bool) + averageBitsPerSec + setEnabled(bool) + + + 333 + 55 + + + 395 + 56 + + + + diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index c98cd52..5d02729 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -34,6 +34,8 @@ QRect StreamConfigDialog::lastGeometry; int StreamConfigDialog::lastTopLevelTabIndex = 0; int StreamConfigDialog::lastProtocolDataIndex = 0; +static const uint kEthFrameOverHead = 20; + StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) { @@ -158,8 +160,8 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, // TODO(MED): //! \todo Enable navigation of streams - pbPrev->setDisabled(true); - pbNext->setDisabled(true); + pbPrev->setHidden(true); + pbNext->setHidden(true); //! \todo Support Goto Stream Id leStreamId->setHidden(true); disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); @@ -266,6 +268,8 @@ void StreamConfigDialog::setupUiExtra() lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); + /* ** Setup Connections */ @@ -563,6 +567,13 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) break; } + // Stream Control + case 2: + { + StoreCurrentStream(); + break; + } + // Packet View case 3: { @@ -915,10 +926,14 @@ void StreamConfigDialog::LoadCurrentStream() leNumPackets->setText(QString().setNum(mpStream->numPackets())); leNumBursts->setText(QString().setNum(mpStream->numBursts())); lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); - lePacketsPerSec->setText(QString().setNum(mpStream->packetRate())); - leBurstsPerSec->setText(QString().setNum(mpStream->burstRate())); + lePacketsPerSec->setText( + QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); + leBurstsPerSec->setText( + QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); // TODO(MED): Change this when we support goto to specific stream leStreamId->setText(QString("0")); + + leGapIsg->setText("0.0"); } qDebug("loading stream done"); } @@ -964,8 +979,10 @@ void StreamConfigDialog::StoreCurrentStream() pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); - pStream->setPacketRate(lePacketsPerSec->text().toDouble(&isOk)); - pStream->setBurstRate(leBurstsPerSec->text().toDouble(&isOk)); + pStream->setPacketRate( + QLocale().toDouble(lePacketsPerSec->text(), &isOk)); + pStream->setBurstRate( + QLocale().toDouble(leBurstsPerSec->text(), &isOk)); } } @@ -980,6 +997,99 @@ void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) #endif } +void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) +{ + if (checked) + on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); +} + +void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) +{ + if (checked) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &text) +{ + if (rbSendBursts->isChecked()) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); + + if (rbPacketsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("0.0")); + leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); + } +} + +void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = burstsPerSec * + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + if (rbBurstsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); + leGapIpg->setText(QString("0.0")); + } + qDebug("end of %s", __FUNCTION__); +} + +void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk)/ + double((frameLen+kEthFrameOverHead)*8); + lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); + } + else if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk)/ + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); + } +} + void StreamConfigDialog::on_pbOk_clicked() { QString log; diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index 8321eba..a3214c3 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -127,6 +127,15 @@ private slots: // "Protocol Data" related void on_tbProtocolData_currentChanged(int index); + // "Stream Control" related + void on_rbPacketsPerSec_toggled(bool checked); + void on_rbBurstsPerSec_toggled(bool checked); + + void on_lePacketsPerBurst_textChanged(const QString &text); + void on_lePacketsPerSec_textChanged(const QString &text); + void on_leBurstsPerSec_textChanged(const QString &text); + void on_leBitsPerSec_textEdited(const QString &text); + void on_pbPrev_clicked(); void on_pbNext_clicked(); diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 4f232dd..50b91a1 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -42,7 +42,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 0 + 2 @@ -146,8 +146,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 592 - 269 + 586 + 248 @@ -798,7 +798,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Numbers @@ -833,7 +833,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Number of Bursts - leNumPackets + leNumBursts @@ -853,7 +853,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff Packets per Burst - leNumPackets + lePacketsPerBurst @@ -870,6 +870,75 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Bursts/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bits/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + @@ -939,85 +1008,20 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Qt::Horizontal - - - - 41 - 20 - - - - - + Qt::Horizontal - - - - Rate - - - false - - - false - - - - - - Packets/Sec - - - leNumPackets - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Bursts/Sec - - - leNumPackets - - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + true - Gaps + Gaps (in seconds) false @@ -1026,13 +1030,13 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff false - + - icons/gaps.png + :/icons/gaps.png @@ -1042,11 +1046,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff ISG - leNumPackets + leGapIsg - + false @@ -1056,28 +1060,18 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - IPG - - - leNumPackets - - - - + IBG - leNumPackets + leGapIbg - - + + false @@ -1086,8 +1080,18 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - + + + + IPG + + + leGapIpg + + + + + false @@ -1099,28 +1103,15 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + Qt::Vertical - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 + 153 + 21 @@ -1276,8 +1267,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff reject() - 558 - 453 + 623 + 496 533 @@ -1292,44 +1283,44 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 169 - 207 + 293 + 137 - 169 - 207 + 293 + 169 rbSendPackets toggled(bool) - lePacketsPerSec + rbPacketsPerSec setEnabled(bool) - 125 - 207 + 30 + 66 - 125 - 207 + 71 + 250 rbSendBursts toggled(bool) - leBurstsPerSec + rbBurstsPerSec setEnabled(bool) - 125 - 207 + 30 + 91 - 125 - 207 + 71 + 310 @@ -1340,12 +1331,92 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 125 - 207 + 30 + 91 - 156 - 207 + 134 + 177 + + + + + rbPacketsPerSec + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 48 + 252 + + + 39 + 278 + + + + + rbBurstsPerSec + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 51 + 303 + + + 54 + 339 + + + + + rbBitsPerSec + toggled(bool) + leBitsPerSec + setEnabled(bool) + + + 82 + 356 + + + 103 + 372 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setChecked(bool) + + + 95 + 70 + + + 154 + 255 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setChecked(bool) + + + 96 + 98 + + + 173 + 299 diff --git a/common/streambase.cpp b/common/streambase.cpp index 720b801..93d82d1 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -258,6 +258,19 @@ bool StreamBase::setFrameLenMax(quint16 frameLenMax) return true; } +/*! Convenience Function */ +quint16 StreamBase::frameLenAvg() const +{ + quint16 avgFrameLen; + + if (lenMode() == e_fl_fixed) + avgFrameLen = frameLen(); + else + avgFrameLen = (frameLenMin() + frameLenMax())/2; + + return avgFrameLen; +} + StreamBase::SendUnit StreamBase::sendUnit() const { return (StreamBase::SendUnit) mControl->unit(); @@ -347,6 +360,44 @@ bool StreamBase::setBurstRate(double burstsPerSec) return true; } +/*! Convenience Function */ +double StreamBase::averagePacketRate() const +{ + double avgPacketRate; + + switch (sendUnit()) + { + case e_su_bursts: + avgPacketRate = burstRate() * burstSize(); + break; + case e_su_packets: + avgPacketRate = packetRate(); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return avgPacketRate; +} + +/*! Convenience Function */ +bool StreamBase::setAveragePacketRate(double packetsPerSec) +{ + switch (sendUnit()) + { + case e_su_bursts: + setBurstRate(packetsPerSec/double(burstSize())); + break; + case e_su_packets: + setPacketRate(packetsPerSec); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return true; +} + bool StreamBase::isFrameVariable() const { ProtocolListIterator *iter; diff --git a/common/streambase.h b/common/streambase.h index 0d1e53b..1d6c5f5 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -107,6 +107,8 @@ public: quint16 frameLenMax() const; bool setFrameLenMax(quint16 frameLenMax); + quint16 frameLenAvg() const; + SendUnit sendUnit() const; bool setSendUnit(SendUnit sendUnit); @@ -131,6 +133,9 @@ public: double burstRate() const; bool setBurstRate(double burstsPerSec); + double averagePacketRate() const; + bool setAveragePacketRate(double packetsPerSec); + bool isFrameVariable() const; bool isFrameSizeVariable() const; int frameProtocolLength(int frameIndex) const; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 193a480..f160143 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -145,8 +145,8 @@ void AbstractPort::updatePacketList() if (streamList_[i]->burstRate() > 0) { ibg = 1e9/double(streamList_[i]->burstRate()); - ibg1 = ceil(ibg); - ibg2 = floor(ibg); + ibg1 = long(ceil(ibg)); + ibg2 = long(floor(ibg)); nb1 = long((ibg - double(ibg2)) * double(numBursts)); nb2= numBursts - nb1; } @@ -157,8 +157,8 @@ void AbstractPort::updatePacketList() if (streamList_[i]->packetRate() > 0) { ipg = 1e9/double(streamList_[i]->packetRate()); - ipg1 = ceil(ipg); - ipg2 = floor(ipg); + ipg1 = long(ceil(ipg)); + ipg2 = long(floor(ipg)); np1 = long((ipg - double(ipg2)) * double(numPackets)); np2= numPackets - np1; } @@ -207,7 +207,7 @@ void AbstractPort::updatePacketList() while (nsec >= 1e9) { sec++; - nsec -= 1e9; + nsec -= long(1e9); } } // for (numPackets) @@ -215,7 +215,7 @@ void AbstractPort::updatePacketList() while (nsec >= 1e9) { sec++; - nsec -= 1e9; + nsec -= long(1e9); } } // for (numBursts) From ca7a264b365964b69c0ed335acb987c4964c53fa Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 31 Aug 2011 18:16:42 +0530 Subject: [PATCH 176/294] Implemented Interleaved streams transmit mode. Changed port rate averaging algo. Fixes issue 12 --- client/ostinato.pro | 3 + client/port.cpp | 136 +++++++++++++++---- client/port.h | 5 +- client/portconfigdialog.cpp | 57 ++++++++ client/portconfigdialog.h | 39 ++++++ client/portconfigdialog.ui | 109 +++++++++++++++ client/portgroup.cpp | 7 +- client/portgroup.h | 2 +- client/portmodel.cpp | 1 + client/portswindow.cpp | 39 +++++- client/portswindow.h | 1 + client/portswindow.ui | 5 + client/streamconfigdialog.cpp | 29 +++- client/streamconfigdialog.ui | 118 ++++++++++------ client/streammodel.cpp | 7 +- client/streammodel.h | 2 +- common/protocol.proto | 6 + server/abstractport.cpp | 247 +++++++++++++++++++++++++++++++++- server/abstractport.h | 6 +- server/drone.pro | 1 + server/pcapport.cpp | 2 +- server/pcapport.h | 11 +- 22 files changed, 745 insertions(+), 88 deletions(-) create mode 100644 client/portconfigdialog.cpp create mode 100644 client/portconfigdialog.h create mode 100644 client/portconfigdialog.ui diff --git a/client/ostinato.pro b/client/ostinato.pro index c12c781..0967390 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -33,6 +33,7 @@ HEADERS += \ mainwindow.h \ packetmodel.h \ port.h \ + portconfigdialog.h \ portgroup.h \ portgrouplist.h \ portmodel.h \ @@ -49,6 +50,7 @@ HEADERS += \ FORMS += \ about.ui \ mainwindow.ui \ + portconfigdialog.ui \ portstatsfilter.ui \ portstatswindow.ui \ portswindow.ui \ @@ -63,6 +65,7 @@ SOURCES += \ mainwindow.cpp \ packetmodel.cpp \ port.cpp \ + portconfigdialog.cpp \ portgroup.cpp \ portgrouplist.cpp \ portmodel.cpp \ diff --git a/client/port.cpp b/client/port.cpp index 79bc8b5..7f1b4c9 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -32,6 +32,8 @@ extern QMainWindow *mainWindow; uint Port::mAllocStreamId = 0; +static const int kEthOverhead = 20; + uint Port::newStreamId() { return mAllocStreamId++; @@ -82,23 +84,36 @@ void Port::recalculateAverageRates() double r = s->averagePacketRate(); pps += r; - bps += r * s->frameLenAvg() * 8; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; - if (s->nextWhat() == Stream::e_nw_stop) + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { - avgPacketsPerSec_ = pps/n; - avgBitsPerSec_ = bps/n; + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; } else - avgPacketsPerSec_ = avgBitsPerSec_ = 0; + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; - qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, - avgPacketsPerSec_, avgBitsPerSec_); + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); @@ -106,76 +121,145 @@ void Port::recalculateAverageRates() void Port::setAveragePacketRate(double packetsPerSec) { + double rate; double pps = 0; double bps = 0; int n = 0; + qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); foreach (Stream* s, mStreams) { if (!s->isEnabled()) continue; - s->setAveragePacketRate(packetsPerSec); + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) * + (packetsPerSec - avgPacketsPerSec_)); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); double r = s->averagePacketRate(); pps += r; - bps += r * s->frameLenAvg() * 8; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; - if (s->nextWhat() == Stream::e_nw_stop) + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { - avgPacketsPerSec_ = pps/n; - avgBitsPerSec_ = bps/n; + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; } else - avgPacketsPerSec_ = avgBitsPerSec_ = 0; + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; - qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, - avgPacketsPerSec_, avgBitsPerSec_); + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); - } void Port::setAverageBitRate(double bitsPerSec) { + double rate; double pps = 0; double bps = 0; int n = 0; + qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); foreach (Stream* s, mStreams) { if (!s->isEnabled()) continue; - s->setAveragePacketRate(bitsPerSec/s->frameLenAvg()); + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); + qDebug("rate = %g", rate); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) + * ((bitsPerSec - avgBitsPerSec_) + / ((s->frameLenAvg()+kEthOverhead)*8))); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); double r = s->averagePacketRate(); pps += r; - bps += r * s->frameLenAvg() * 8; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; - if (s->nextWhat() == Stream::e_nw_stop) + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { - avgPacketsPerSec_ = pps/n; - avgBitsPerSec_ = bps/n; + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; } else - avgPacketsPerSec_ = avgBitsPerSec_ = 0; - - qDebug("%s: avgPps = %g avgBps = %g", __FUNCTION__, - avgPacketsPerSec_, avgBitsPerSec_); + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); - } bool Port::newStreamAt(int index, OstProto::Stream const *stream) diff --git a/client/port.h b/client/port.h index 52cd72a..10c2803 100644 --- a/client/port.h +++ b/client/port.h @@ -44,7 +44,8 @@ class Port : public QObject { QString mUserAlias; // user defined double avgPacketsPerSec_; - double avgBitsPerSec_; + double avgBitsPerSec_; + int numActiveStreams_; QList mLastSyncStreamList; QList mStreams; // sorted by stream's ordinal value @@ -76,6 +77,8 @@ public: { return (d.is_enabled()?AdminEnable:AdminDisable); } bool hasExclusiveControl() { return d.is_exclusive_control(); } + OstProto::TransmitMode transmitMode() + { return d.transmit_mode(); } double averagePacketRate() { return avgPacketsPerSec_; } double averageBitRate() diff --git a/client/portconfigdialog.cpp b/client/portconfigdialog.cpp new file mode 100644 index 0000000..17adeb5 --- /dev/null +++ b/client/portconfigdialog.cpp @@ -0,0 +1,57 @@ +/* +Copyright (C) 2011 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 "portconfigdialog.h" + +PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent) + : QDialog(parent), portConfig_(portConfig) +{ + qDebug("In %s", __FUNCTION__); + + setupUi(this); + + switch(portConfig_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + sequentialStreamsButton->setChecked(true); + break; + case OstProto::kInterleavedTransmit: + interleavedStreamsButton->setChecked(true); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } + + exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); +} + +void PortConfigDialog::accept() +{ + if (sequentialStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kSequentialTransmit); + else if (interleavedStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit); + else + Q_ASSERT(false); // Unreachable!!! + + portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked()); + + QDialog::accept(); +} diff --git a/client/portconfigdialog.h b/client/portconfigdialog.h new file mode 100644 index 0000000..4d14b0b --- /dev/null +++ b/client/portconfigdialog.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PORT_CONFIG_DIALOG_H +#define _PORT_CONFIG_DIALOG_H + +#include "ui_portconfigdialog.h" +#include "protocol.pb.h" +#include + +class PortConfigDialog : public QDialog, public Ui::PortConfigDialog +{ +public: + PortConfigDialog(OstProto::Port &portConfig, QWidget *parent); + +private: + virtual void accept(); + + OstProto::Port &portConfig_; +}; + +#endif + diff --git a/client/portconfigdialog.ui b/client/portconfigdialog.ui new file mode 100644 index 0000000..954b827 --- /dev/null +++ b/client/portconfigdialog.ui @@ -0,0 +1,109 @@ + + PortConfigDialog + + + + 0 + 0 + 244 + 160 + + + + Port Config + + + + + + Transmit Mode + + + + + + Sequential Streams + + + true + + + + + + + Interleaved Streams + + + + + + + + + + Exclusive Control + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PortConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portgroup.cpp b/client/portgroup.cpp index bbea61d..9762fac 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -340,7 +340,7 @@ void PortGroup::processModifyStreamAck(int portIndex, delete controller; } -void PortGroup::modifyPort(int portIndex, bool isExclusive) +void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) { OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; OstProto::Ack *ack = new OstProto::Ack; @@ -353,8 +353,8 @@ void PortGroup::modifyPort(int portIndex, bool isExclusive) mainWindow->setDisabled(true); OstProto::Port *port = portConfigList->add_port(); + port->CopyFrom(portConfig); port->mutable_port_id()->set_id(mPorts[portIndex]->id()); - port->set_is_exclusive_control(isExclusive); PbRpcController *controller = new PbRpcController(portConfigList, ack); serviceStub->modifyPort(controller, portConfigList, ack, @@ -412,9 +412,10 @@ void PortGroup::processUpdatedPortConfig(PbRpcController *controller) id = portConfigList->port(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + + emit portGroupDataChanged(mPortGroupId, id); } - emit portGroupDataChanged(mPortGroupId); _exit: mainWindow->setEnabled(true); diff --git a/client/portgroup.h b/client/portgroup.h index 254e731..b4d7580 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -94,7 +94,7 @@ public: void processDeleteStreamAck(PbRpcController *controller); void processModifyStreamAck(int portIndex, PbRpcController *controller); - void modifyPort(int portId, bool isExclusive); + void modifyPort(int portId, OstProto::Port portConfig); void processModifyPortAck(PbRpcController *controller); void processUpdatedPortConfig(PbRpcController *controller); diff --git a/client/portmodel.cpp b/client/portmodel.cpp index 1d13eda..0ef055e 100644 --- a/client/portmodel.cpp +++ b/client/portmodel.cpp @@ -296,6 +296,7 @@ void PortModel::when_portGroupDataChanged(int portGroupId, int portId) QModelIndex index; int row; + qDebug("portGroupId = %d, portId = %d", portGroupId, portId); if (portId == 0xFFFF) row = pgl->indexOfPortGroup(portGroupId); else diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 5ed280a..1f3bc4d 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -20,6 +20,7 @@ along with this program. If not, see #include "portswindow.h" #include "abstractfileformat.h" +#include "portconfigdialog.h" #include "streamconfigdialog.h" #include "streamlistdelegate.h" @@ -54,6 +55,7 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvPortList->addAction(actionDisconnect_Port_Group); tvPortList->addAction(actionExclusive_Control); + tvPortList->addAction(actionPort_Configuration); // Populate StramList Context Menu Actions tvStreamList->addAction(actionNew_Stream); @@ -156,6 +158,8 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& current, updatePortViewActions(current); updateStreamViewActions(); + qDebug("In %s", __FUNCTION__); + if (previous.isValid() && plm->isPort(previous)) { disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), @@ -186,6 +190,7 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& current, void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { + qDebug("In %s", __FUNCTION__); #if 0 // not sure why the >= <= operators are not overloaded in QModelIndex if ((tvPortList->currentIndex() >= topLeft) && (tvPortList->currentIndex() <= bottomRight)) @@ -195,7 +200,10 @@ void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, (((tvPortList->currentIndex() < bottomRight)) || (tvPortList->currentIndex() == bottomRight))) { - updatePortViewActions(tvPortList->currentIndex()); + // Update UI to reflect potential change in exclusive mode, + // transmit mode et al + when_portView_currentChanged(tvPortList->currentIndex(), + tvPortList->currentIndex()); } } @@ -223,7 +231,7 @@ void PortsWindow::on_averageBitsPerSec_editingFinished() Q_ASSERT(plm->isPort(current)); bool isOk; - double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk)/8; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); plm->port(current).setAverageBitRate(bps); } @@ -300,6 +308,7 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) actionDisconnect_Port_Group->setDisabled(true); actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); goto _EXIT; } @@ -311,6 +320,7 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) actionDelete_Port_Group->setEnabled(true); actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); switch(plm->portGroup(current).state()) { @@ -349,6 +359,7 @@ void PortsWindow::updatePortViewActions(const QModelIndex& current) actionExclusive_Control->setChecked(true); else actionExclusive_Control->setChecked(false); + actionPort_Configuration->setEnabled(true); } _EXIT: @@ -458,7 +469,29 @@ void PortsWindow::on_actionExclusive_Control_triggered(bool checked) QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (plm->isPort(current)) - plm->portGroup(current.parent()).modifyPort(current.row(), checked); + { + OstProto::Port config; + + config.set_is_exclusive_control(checked); + plm->portGroup(current.parent()).modifyPort(current.row(), config); + } +} + +void PortsWindow::on_actionPort_Configuration_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (!plm->isPort(current)) + return; + + OstProto::Port config; + config.set_transmit_mode(plm->port(current).transmitMode()); + config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); + + PortConfigDialog dialog(config, this); + + if (dialog.exec() == QDialog::Accepted) + plm->portGroup(current.parent()).modifyPort(current.row(), config); } void PortsWindow::on_actionNew_Stream_triggered() diff --git a/client/portswindow.h b/client/portswindow.h index 3921296..5c071f0 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -70,6 +70,7 @@ private slots: void on_actionDisconnect_Port_Group_triggered(); void on_actionExclusive_Control_triggered(bool checked); + void on_actionPort_Configuration_triggered(); void on_actionNew_Stream_triggered(); void on_actionEdit_Stream_triggered(); diff --git a/client/portswindow.ui b/client/portswindow.ui index 1441012..a5d9261 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -253,6 +253,11 @@ Save Streams ... + + + Port Configuration ... + + diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 5d02729..26319ea 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -165,8 +165,22 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, //! \todo Support Goto Stream Id leStreamId->setHidden(true); disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); - //! \todo Support Continuous Mode - rbModeContinuous->setDisabled(true); + + switch(mPort.transmitMode()) + { + case OstProto::kSequentialTransmit: + rbModeFixed->setChecked(true); + rbModeContinuous->setDisabled(true); + break; + case OstProto::kInterleavedTransmit: + rbModeContinuous->setChecked(true); + rbModeFixed->setDisabled(true); + + nextWhat->setHidden(true); + break; + default: + Q_ASSERT(false); // Unreachable + } // Finally, restore the saved last geometry and selected tab for the // various tab widgets @@ -1098,7 +1112,16 @@ void StreamConfigDialog::on_pbOk_clicked() // Store dialog contents into stream StoreCurrentStream(); - if (!mpStream->preflightCheck(log)) + if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) + && (mpStream->isFrameVariable())) + { + log += "In 'Interleaved Streams' transmit mode, the count for " + "varying fields at transmit time may not be same as configured\n"; + } + + mpStream->preflightCheck(log); + + if (log.length()) { if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 50b91a1..be71e47 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -42,7 +42,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - 2 + 0 @@ -146,8 +146,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 0 0 - 586 - 248 + 592 + 269 @@ -940,7 +940,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - + After this stream @@ -982,6 +982,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff + + + + Qt::Horizontal + + + + 20 + 41 + + + + @@ -1008,14 +1021,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff - - - - Qt::Horizontal - - - - + true @@ -1283,12 +1289,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 293 - 137 + 463 + 143 - 293 - 169 + 463 + 177 @@ -1300,11 +1306,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 30 - 66 + 68 - 71 - 250 + 299 + 82 @@ -1316,11 +1322,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 30 - 91 + 95 - 71 - 310 + 299 + 132 @@ -1332,11 +1338,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 30 - 91 + 95 134 - 177 + 189 @@ -1347,12 +1353,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 48 - 252 + 299 + 82 - 39 - 278 + 299 + 108 @@ -1363,12 +1369,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 51 - 303 + 299 + 132 - 54 - 339 + 299 + 158 @@ -1379,12 +1385,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff setEnabled(bool) - 82 - 356 + 299 + 182 - 103 - 372 + 299 + 208 @@ -1399,8 +1405,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 70 - 154 - 255 + 299 + 82 @@ -1415,8 +1421,40 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff 98 - 173 - 299 + 299 + 132 + + + + + rbModeContinuous + toggled(bool) + leNumPackets + setDisabled(bool) + + + 73 + 196 + + + 164 + 108 + + + + + rbModeContinuous + toggled(bool) + leNumBursts + setDisabled(bool) + + + 96 + 199 + + + 226 + 155 diff --git a/client/streammodel.cpp b/client/streammodel.cpp index 19942fd..c66f02c 100644 --- a/client/streammodel.cpp +++ b/client/streammodel.cpp @@ -42,7 +42,12 @@ int StreamModel::rowCount(const QModelIndex &parent) const int StreamModel::columnCount(const QModelIndex &/*parent*/) const { - return (int) StreamMaxFields; + int count = StreamMaxFields; + if (mCurrentPort && + (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) + count--; + + return count; } Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const diff --git a/client/streammodel.h b/client/streammodel.h index 9c7e1aa..d559618 100644 --- a/client/streammodel.h +++ b/client/streammodel.h @@ -58,8 +58,8 @@ class StreamModel : public QAbstractTableModel public: enum StreamFields { StreamIcon = 0, - StreamName, StreamStatus, + StreamName, StreamNextWhat, StreamMaxFields diff --git a/common/protocol.proto b/common/protocol.proto index 9ac1c9a..c902610 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -151,6 +151,11 @@ message StreamIdList { repeated StreamId stream_id = 2; } +enum TransmitMode { + kSequentialTransmit = 0; + kInterleavedTransmit = 1; +} + message Port { required PortId port_id = 1; optional string name = 2; @@ -158,6 +163,7 @@ message Port { optional string notes = 4; optional bool is_enabled = 5; optional bool is_exclusive_control = 6; + optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; } message PortConfigList { diff --git a/server/abstractport.cpp b/server/abstractport.cpp index f160143..f375cad 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -17,12 +17,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ +#define __STDC_FORMAT_MACROS + #include "abstractport.h" #include #include #include "../common/streambase.h" +#include +#include #include AbstractPort::AbstractPort(int id, const char *device) @@ -65,6 +69,9 @@ bool AbstractPort::modify(const OstProto::Port &port) data_.set_is_exclusive_control(val); } + if (port.has_transmit_mode()) + data_.set_transmit_mode(port.transmit_mode()); + return ret; } @@ -112,6 +119,22 @@ bool AbstractPort::deleteStream(int streamId) } void AbstractPort::updatePacketList() +{ + switch(data_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + updatePacketListSequential(); + break; + case OstProto::kInterleavedTransmit: + updatePacketListInterleaved(); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } +} + +void AbstractPort::updatePacketListSequential() { int len; bool isVariable; @@ -237,8 +260,9 @@ void AbstractPort::updatePacketList() returnToQIdx = 0; */ - setPacketListLoopMode(true, streamList_[i]->sendUnit() == - StreamBase::e_su_bursts ? ibg1 : ipg1); + setPacketListLoopMode(true, 0, + streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg1 : ipg1); goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_next: @@ -257,6 +281,225 @@ _stop_no_more_pkts: isSendQueueDirty_ = false; } +void AbstractPort::updatePacketListInterleaved() +{ + int numStreams = 0; + quint64 minGap = ULONG_LONG_MAX; + quint64 duration = quint64(1e9); + QList ibg1, ibg2; + QList nb1, nb2; + QList ipg1, ipg2; + QList np1, np2; + QList schedSec, schedNsec; + QList pktCount, burstCount; + QList burstSize; + QList isVariable; + QList pktBuf; + QList pktLen; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (!streamList_[i]->isEnabled()) + continue; + + double numBursts = 0; + double numPackets = 0; + + quint64 _burstSize; + double ibg = 0; + quint64 _ibg1 = 0, _ibg2 = 0; + quint64 _nb1 = 0, _nb2 = 0; + double ipg = 0; + quint64 _ipg1 = 0, _ipg2 = 0; + quint64 _np1 = 0, _np2 = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->burstRate(); + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + _ibg1 = quint64(ceil(ibg)); + _ibg2 = quint64(floor(ibg)); + _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); + _nb2 = quint64(numBursts) - _nb1; + _burstSize = streamList_[i]->burstSize(); + } + break; + case OstProto::StreamControl::e_su_packets: + numPackets = streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + _ipg1 = llrint(ceil(ipg)); + _ipg2 = quint64(floor(ipg)); + _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); + _np2 = quint64(numPackets) - _np1; + _burstSize = 1; + } + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, _ibg1); + qDebug("nb1 = %" PRIu64, _nb1); + qDebug("ibg2 = %" PRIu64, _ibg2); + qDebug("nb2 = %" PRIu64 "\n", _nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, _ipg1); + qDebug("np1 = %" PRIu64, _np1); + qDebug("ipg2 = %" PRIu64, _ipg2); + qDebug("np2 = %" PRIu64 "\n", _np2); + + + if (_ibg2 && (_ibg2 < minGap)) + minGap = _ibg2; + + if (_ibg1 && (_ibg1 > duration)) + duration = _ibg1; + + ibg1.append(_ibg1); + ibg2.append(_ibg2); + + nb1.append(_nb1); + nb2.append(_nb1); + + burstSize.append(_burstSize); + + if (_ipg2 && (_ipg2 < minGap)) + minGap = _ipg2; + + if (_np1) + { + if (_ipg1 && (_ipg1 > duration)) + duration = _ipg1; + } + else + { + if (_ipg2 && (_ipg2 > duration)) + duration = _ipg2; + } + + ipg1.append(_ipg1); + ipg2.append(_ipg2); + + np1.append(_np1); + np2.append(_np1); + + schedSec.append(0); + schedNsec.append(0); + + pktCount.append(0); + burstCount.append(0); + + if (streamList_[i]->isFrameVariable()) + { + isVariable.append(true); + pktBuf.append(QByteArray()); + pktLen.append(0); + } + else + { + isVariable.append(false); + pktBuf.append(QByteArray()); + pktBuf.last().resize(kMaxPktSize); + pktLen.append(streamList_[i]->frameValue( + (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); + } + + numStreams++; + } // for i + + qDebug("minGap = %" PRIu64, minGap); + qDebug("duration = %" PRIu64, duration); + + uchar* buf; + int len; + quint64 durSec = duration/ulong(1e9); + quint64 durNsec = duration % ulong(1e9); + quint64 sec = 0; + quint64 nsec = 0; + quint64 lastPktTxSec = 0; + quint64 lastPktTxNsec = 0; + do + { + for (int i = 0; i < numStreams; i++) + { + // If a packet is not scheduled yet, look at the next stream + if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) + continue; + + for (uint j = 0; j < burstSize[i]; j++) + { + if (isVariable.at(i)) + { + buf = pktBuf_; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), + pktCount[i]); + } + else + { + buf = (uchar*) pktBuf.at(i).data(); + len = pktLen.at(i); + } + + if (len <= 0) + continue; + + qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); + appendToPacketList(sec, nsec, buf, len); + lastPktTxSec = sec; + lastPktTxNsec = nsec; + + pktCount[i]++; + schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? + ipg1.at(i) : ipg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + burstCount[i]++; + schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? + ibg1.at(i) : ibg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + nsec += minGap; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } while ((sec < durSec) || (nsec < durNsec)); + + quint64 delaySec = durSec - lastPktTxSec; + quint64 delayNsec = durNsec - lastPktTxNsec; + qDebug("loop Delay = %" PRIu64 "/%" PRIu64, delaySec, delayNsec); + setPacketListLoopMode(true, durSec - lastPktTxSec, durNsec - lastPktTxNsec); + isSendQueueDirty_ = false; +} + void AbstractPort::stats(PortStats *stats) { stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; diff --git a/server/abstractport.h b/server/abstractport.h index 18d0b1e..c04affe 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -73,7 +73,8 @@ public: virtual void clearPacketList() = 0; virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; - virtual void setPacketListLoopMode(bool loop, long nsecDelay) = 0; + virtual void setPacketListLoopMode(bool loop, + long secDelay, long nsecDelay) = 0; void updatePacketList(); virtual void startTransmit() = 0; @@ -89,6 +90,9 @@ public: void resetStats() { epochStats_ = stats_; } protected: + void updatePacketListSequential(); + void updatePacketListInterleaved(); + bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; diff --git a/server/drone.pro b/server/drone.pro index a317414..e235c24 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -24,6 +24,7 @@ win32 { LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } +LIBS += -lm LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 RESOURCES += drone.qrc diff --git a/server/pcapport.cpp b/server/pcapport.cpp index fba37e1..3f1434b 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -295,7 +295,7 @@ void PcapPort::PortTransmitter::clearPacketList() pcap_send_queue *sq = sendQueueList_.takeFirst(); pcap_sendqueue_destroy(sq); } - setPacketListLoopMode(false, 0); + setPacketListLoopMode(false, 0, 0); } bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, diff --git a/server/pcapport.h b/server/pcapport.h index 53e5867..cd96f6b 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -40,14 +40,15 @@ public: virtual void clearPacketList() { transmitter_->clearPacketList(); - setPacketListLoopMode(false, 0); + setPacketListLoopMode(false, 0, 0); } virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) { return transmitter_->appendToPacketList(sec, nsec, packet, length); } - virtual void setPacketListLoopMode(bool loop, long nsecDelay) { - transmitter_->setPacketListLoopMode(loop, nsecDelay); + virtual void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) + { + transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); } virtual void startTransmit() { @@ -97,9 +98,9 @@ protected: void clearPacketList(); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); - void setPacketListLoopMode(bool loop, long nsecDelay) { + void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) { returnToQIdx_ = loop ? 0 : -1; - loopDelay_ = nsecDelay/1000; + loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; } void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); From 18e7b9b49c93176f8a893cc185859f0faca455a8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 26 Sep 2011 20:47:27 +0530 Subject: [PATCH 177/294] Snapshot before refactoring the send queue data structure --- common/abstractprotocol.cpp | 65 ++++++++++++++++++++++++ common/abstractprotocol.h | 4 ++ common/ip4.cpp | 13 +++++ common/ip4.h | 1 + common/streambase.cpp | 21 ++++++++ common/streambase.h | 1 + server/abstractport.cpp | 99 +++++++++++++++++++------------------ server/abstractport.h | 1 + server/pcapport.cpp | 97 ++++++++++++++++++++++++++++-------- server/pcapport.h | 11 +++++ 10 files changed, 244 insertions(+), 69 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index ef80783..7aa64ac 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -569,6 +569,23 @@ bool AbstractProtocol::isProtocolFrameSizeVariable() const return false; } +/*! + Returns the number of frames required for the protocol to vary its fields + + This is the lowest common multiple (LCM) of the counts of all the varying + fields in the protocol. Use the AbstractProtocol::lcm() static utility + function to calculate the LCM. + + The default implementation returns 1 implying that the protocol has no + varying fields. A subclass should reimplement if it has varying fields + e.g. an IP protocol that increments/decrements the IP address with + every packet +*/ +int AbstractProtocol::protocolFrameVariableCount() const +{ + return 1; +} + /*! Returns true if the payload content for a protocol varies at run-time, false otherwise @@ -823,3 +840,51 @@ out: this function. See the SampleProtocol for an example */ +// Stein's binary GCD algo - from wikipedia +quint64 AbstractProtocol::gcd(quint64 u, quint64 v) +{ + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Let shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + quint64 diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +quint64 AbstractProtocol::lcm(quint64 u, quint64 v) +{ + /* FIXME: LCM(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + return (u * v)/gcd(u, v); +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 07350cc..eb6dd81 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -139,6 +139,7 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; @@ -156,6 +157,9 @@ public: virtual QWidget* configWidget() = 0; virtual void loadConfigWidget() = 0; virtual void storeConfigWidget() = 0; + + static quint64 lcm(quint64 u, quint64 v); + static quint64 gcd(quint64 u, quint64 v); }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); diff --git a/common/ip4.cpp b/common/ip4.cpp index 197cc8e..a7d6ef7 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -611,6 +611,19 @@ bool Ip4Protocol::isProtocolFrameValueVariable() const return false; } +int Ip4Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.src_ip_count()); + + if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.dst_ip_count()); + + return count; +} + quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { diff --git a/common/ip4.h b/common/ip4.h index 006d9ca..6f89b09 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -102,6 +102,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; diff --git a/common/streambase.cpp b/common/streambase.cpp index 93d82d1..8c049e2 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -440,6 +440,27 @@ _exit: return true; } +int StreamBase::frameVariableCount() const +{ + ProtocolListIterator *iter; + quint64 frameCount = 1; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + int count; + + proto = iter->next(); + count = proto->protocolFrameVariableCount(); + + frameCount = AbstractProtocol::lcm(frameCount, count); + } + delete iter; + + return frameCount; +} + // frameProtocolLength() returns the sum of all the individual protocol sizes // which may be different from frameLen() int StreamBase::frameProtocolLength(int frameIndex) const diff --git a/common/streambase.h b/common/streambase.h index 1d6c5f5..9ef3ba1 100644 --- a/common/streambase.h +++ b/common/streambase.h @@ -138,6 +138,7 @@ public: bool isFrameVariable() const; bool isFrameSizeVariable() const; + int frameVariableCount() const; int frameProtocolLength(int frameIndex) const; int frameCount() const; int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index f375cad..8d1fa00 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -21,10 +21,12 @@ along with this program. If not, see #include "abstractport.h" +#include "../common/streambase.h" +#include "../common/abstractprotocol.h" + #include #include -#include "../common/streambase.h" #include #include #include @@ -136,8 +138,6 @@ void AbstractPort::updatePacketList() void AbstractPort::updatePacketListSequential() { - int len; - bool isVariable; long sec = 0; long nsec = 0; @@ -152,7 +152,8 @@ void AbstractPort::updatePacketListSequential() { if (streamList_[i]->isEnabled()) { - long numPackets, numBursts; + long n, x, y; + long burstSize; double ibg = 0; long ibg1 = 0, ibg2 = 0; long nb1 = 0, nb2 = 0; @@ -163,27 +164,32 @@ void AbstractPort::updatePacketListSequential() switch (streamList_[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: - numBursts = streamList_[i]->numBursts(); - numPackets = streamList_[i]->burstSize(); + burstSize = streamList_[i]->burstSize(); + x = AbstractProtocol::lcm(streamList_[i]->frameVariableCount(), + burstSize); + n = ulong(burstSize * streamList_[i]->burstRate()) / x; + y = ulong(burstSize * streamList_[i]->burstRate()) % x; if (streamList_[i]->burstRate() > 0) { ibg = 1e9/double(streamList_[i]->burstRate()); ibg1 = long(ceil(ibg)); ibg2 = long(floor(ibg)); - nb1 = long((ibg - double(ibg2)) * double(numBursts)); - nb2= numBursts - nb1; + nb1 = long((ibg - double(ibg2)) * double(x)); + nb2 = x - nb1; } break; case OstProto::StreamControl::e_su_packets: - numBursts = 1; - numPackets = streamList_[i]->numPackets(); + x = streamList_[i]->frameVariableCount(); + n = streamList_[i]->numPackets() / x; + y = streamList_[i]->numPackets() % x; + burstSize = x + y; if (streamList_[i]->packetRate() > 0) { ipg = 1e9/double(streamList_[i]->packetRate()); ipg1 = long(ceil(ipg)); ipg2 = long(floor(ipg)); - np1 = long((ipg - double(ipg2)) * double(numPackets)); - np2= numPackets - np1; + np1 = long((ipg - double(ipg2)) * double(x)); + np2 = x - np1; } break; default: @@ -191,56 +197,51 @@ void AbstractPort::updatePacketListSequential() streamList_[i]->sendUnit()); continue; } - qDebug("numBursts = %ld, numPackets = %ld\n", - numBursts, numPackets); - qDebug("ibg = %g, ibg1/nb1 = %ld/%ld ibg2/nb2 = %ld/%ld\n", + + qDebug("\nframeVariableCount = %d", + streamList_[i]->frameVariableCount()); + qDebug("n = %ld, x = %ld, y = %ld, burstSize = %ld", + n, x, y, burstSize); + qDebug("ibg = %g, ibg1/nb1 = %ld/%ld ibg2/nb2 = %ld/%ld", ibg, ibg1, nb1, ibg2, nb2); qDebug("ipg = %g, ipg1/np1 = %ld/%ld ipg2/np2 = %ld/%ld\n", ipg, ipg1, np1, ipg2, np2); - if (streamList_[i]->isFrameVariable()) - { - isVariable = true; - len = 0; // avoid compiler warning; get len value for each pkt - } - else - { - isVariable = false; - len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), 0); - } + if (n > 1) + loopNextPacketSet(x, n); - for (int j = 0; j < numBursts; j++) + for (int j = 0; j < (x+y); j++) { - for (int k = 0; k < numPackets; k++) + int len; + + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), j); + if (len <= 0) + continue; + + qDebug("q(%d, %d) sec = %lu nsec = %lu", + i, j, sec, nsec); + + appendToPacketList(sec, nsec, pktBuf_, len); + + if ((j % burstSize) == 0) { - if (isVariable) - { - len = streamList_[i]->frameValue(pktBuf_, - sizeof(pktBuf_), j * numPackets + k); - } - if (len <= 0) - continue; - - qDebug("q(%d, %d, %d) sec = %lu nsec = %lu", - i, j, k, sec, nsec); - - appendToPacketList(sec, nsec, pktBuf_, len); - - nsec += (k < np1) ? ipg1 : ipg2; + nsec += (j < nb1) ? ibg1 : ibg2; while (nsec >= 1e9) { sec++; nsec -= long(1e9); } - } // for (numPackets) - - nsec += (j < nb1) ? ibg1 : ibg2; - while (nsec >= 1e9) - { - sec++; - nsec -= long(1e9); } - } // for (numBursts) + else + { + nsec += (j < np1) ? ipg1 : ipg2; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } + } switch(streamList_[i]->nextWhat()) { diff --git a/server/abstractport.h b/server/abstractport.h index c04affe..3d0f3ce 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -71,6 +71,7 @@ public: void setDirty() { isSendQueueDirty_ = true; } virtual void clearPacketList() = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats) = 0; virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; virtual void setPacketListLoopMode(bool loop, diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 3f1434b..da7360c 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -295,43 +295,79 @@ void PcapPort::PortTransmitter::clearPacketList() pcap_send_queue *sq = sendQueueList_.takeFirst(); pcap_sendqueue_destroy(sq); } + sendQueueRepeat_.clear(); + sendQueueGoto_.clear(); + currentSendQueue_ = NULL; + currentPacketSetQIdx_ = -1; + currentPacketSetSize_ = -1; + currentPacketSetRepeat_ = 1; + currentPacketSetCount_ = 0; setPacketListLoopMode(false, 0, 0); } +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats) +{ + // Trigger a new sendQ alloc on next packet + currentSendQueue_ = NULL; + currentPacketSetQIdx_ = sendQueueList_.size(); + currentPacketSetSize_ = size; + currentPacketSetRepeat_ = repeats; + currentPacketSetCount_ = 0; +} + bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, const uchar *packet, int length) { bool op = true; pcap_pkthdr pktHdr; - pcap_send_queue *sendQ; pktHdr.caplen = pktHdr.len = length; pktHdr.ts.tv_sec = sec; pktHdr.ts.tv_usec = nsec/1000; - sendQ = sendQueueList_.isEmpty() ? NULL : sendQueueList_.last(); - - if ((sendQ == NULL) || - (sendQ->len + 2*sizeof(pcap_pkthdr) + length) > sendQ->maxlen) + if ((currentSendQueue_ == NULL) || + (currentSendQueue_->len + 2*sizeof(pcap_pkthdr) + length) + > currentSendQueue_->maxlen) { - // Add a zero len packet at end of sendQ for inter-sendQ timing - if (sendQ) + // Add a zero len packet at end of currentSendQueue_ for + // inter-sendQ timing + if (sendQueueList_.size()) { pcap_pkthdr hdr = pktHdr; hdr.caplen = 0; - pcap_sendqueue_queue(sendQ, &hdr, (u_char*)packet); + pcap_sendqueue_queue(sendQueueList_.last(), &hdr, (u_char*)packet); } //! \todo (LOW): calculate sendqueue size - sendQ = pcap_sendqueue_alloc(1*1024*1024); - sendQueueList_.append(sendQ); + currentSendQueue_ = pcap_sendqueue_alloc(1*1024*1024); - // Validate that the pkt will fit inside the new sendQ - Q_ASSERT((length + sizeof(pcap_pkthdr)) < sendQ->maxlen); + sendQueueList_.append(currentSendQueue_); + sendQueueRepeat_.append(1); + sendQueueGoto_.append(-1); + + // Validate that the pkt will fit inside the new currentSendQueue_ + Q_ASSERT((length + sizeof(pcap_pkthdr)) < currentSendQueue_->maxlen); } - if (pcap_sendqueue_queue(sendQ, &pktHdr, (u_char*) packet) < 0) + if (pcap_sendqueue_queue(currentSendQueue_, &pktHdr, (u_char*) packet) < 0) op = false; + currentPacketSetCount_++; + if (currentPacketSetCount_ == currentPacketSetSize_) + { + qDebug("i=%d, currentPacketSetQIdx_ = %d, currentPacketSetRepeat_ = %d", + sendQueueRepeat_.size(), + currentPacketSetQIdx_, + currentPacketSetRepeat_); + // End current sendQ + sendQueueGoto_.last() = currentPacketSetQIdx_; + sendQueueRepeat_.last() = currentPacketSetRepeat_; + currentPacketSetSize_ = -1; + + // Trigger a new sendQ allocation + currentSendQueue_ = NULL; + } + + return op; } @@ -375,19 +411,40 @@ void PcapPort::PortTransmitter::run() if (sendQueueList_.size() <= 0) return; + for(i = 0; i < sendQueueList_.size(); i++) { + qDebug("sendQ[%d]: rpt = %d, goto = %d", i, + sendQueueRepeat_.at(i), sendQueueGoto_.at(i)); + } + for(i = 0; i < sendQueueList_.size(); i++) { int ret; + _restart: - ret = sendQueueTransmit(handle_, sendQueueList_.at(i), overHead, + int idx = sendQueueGoto_.at(i); + int rpt = sendQueueRepeat_.at(i); + int sqi = i; + + for (int n = 0; n < rpt; n++) + { + Q_ASSERT(sqi <= i); + +#if 0 + if ((n & 0x3F) == 0) + qDebug("i = %d, n = %d/%d, sqi = %d\n", i, n, rpt, sqi); +#endif + ret = sendQueueTransmit(handle_, sendQueueList_.at(sqi), overHead, kSyncTransmit); - if (ret < 0) - { - qDebug("error %d in sendQueueTransmit()", ret); - qDebug("overHead = %ld", overHead); - stop_ = false; - return; + if (ret < 0) + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + return; + } + + sqi = (sqi == i) ? idx : sqi+1; } } diff --git a/server/pcapport.h b/server/pcapport.h index cd96f6b..daf1507 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -42,6 +42,9 @@ public: transmitter_->clearPacketList(); setPacketListLoopMode(false, 0, 0); } + virtual void loopNextPacketSet(qint64 size, qint64 repeats) { + transmitter_->loopNextPacketSet(size, repeats); + } virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) { return transmitter_->appendToPacketList(sec, nsec, packet, length); @@ -96,6 +99,7 @@ protected: PortTransmitter(const char *device); ~PortTransmitter(); void clearPacketList(); + void loopNextPacketSet(qint64 size, qint64 repeats); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) { @@ -112,7 +116,14 @@ protected: int sync); quint64 ticksFreq_; + int currentPacketSetQIdx_; + int currentPacketSetSize_; + int currentPacketSetRepeat_; + int currentPacketSetCount_; + pcap_send_queue* currentSendQueue_; QList sendQueueList_; + QList sendQueueRepeat_; + QList sendQueueGoto_; int returnToQIdx_; long loopDelay_; bool usingInternalStats_; From 204e6efc2a9b4cd4a6983f2bd25c73f952431d90 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 1 Oct 2011 12:28:28 +0530 Subject: [PATCH 178/294] Refactored send queue data structure and associated processing --- common/abstractprotocol.cpp | 3 +- common/streambase.cpp | 4 ++ server/abstractport.cpp | 5 ++ server/pcapport.cpp | 140 ++++++++++++++++++------------------ server/pcapport.h | 38 +++++++--- 5 files changed, 109 insertions(+), 81 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 7aa64ac..8ff9529 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -570,7 +570,8 @@ bool AbstractProtocol::isProtocolFrameSizeVariable() const } /*! - Returns the number of frames required for the protocol to vary its fields + Returns the minimum number of frames required for the protocol to + vary its fields This is the lowest common multiple (LCM) of the counts of all the varying fields in the protocol. Use the AbstractProtocol::lcm() static utility diff --git a/common/streambase.cpp b/common/streambase.cpp index 8c049e2..ab43117 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -454,6 +454,10 @@ int StreamBase::frameVariableCount() const proto = iter->next(); count = proto->protocolFrameVariableCount(); + // correct count for mis-behaving protocols + if (count <= 0) + count = 1; + frameCount = AbstractProtocol::lcm(frameCount, count); } delete iter; diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 8d1fa00..3bf9148 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -161,6 +161,9 @@ void AbstractPort::updatePacketListSequential() long ipg1 = 0, ipg2 = 0; long np1 = 0, np2 = 0; + // We derive n, x, y such that + // n * x + y = total number of packets to be sent + switch (streamList_[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: @@ -209,6 +212,8 @@ void AbstractPort::updatePacketListSequential() if (n > 1) loopNextPacketSet(x, n); + else if (n == 0) + x = 0; for (int j = 0; j < (x+y); j++) { diff --git a/server/pcapport.cpp b/server/pcapport.cpp index da7360c..8a82b1c 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -289,30 +289,28 @@ PcapPort::PortTransmitter::~PortTransmitter() void PcapPort::PortTransmitter::clearPacketList() { Q_ASSERT(!isRunning()); - // \todo lock for sendQueueList - while(sendQueueList_.size()) - { - pcap_send_queue *sq = sendQueueList_.takeFirst(); - pcap_sendqueue_destroy(sq); - } - sendQueueRepeat_.clear(); - sendQueueGoto_.clear(); - currentSendQueue_ = NULL; - currentPacketSetQIdx_ = -1; - currentPacketSetSize_ = -1; - currentPacketSetRepeat_ = 1; - currentPacketSetCount_ = 0; + // \todo lock for packetSequenceList + while(packetSequenceList_.size()) + delete packetSequenceList_.takeFirst(); + + currentPacketSequence_ = NULL; + repeatSequenceStart_ = -1; + repeatSize_ = 0; + packetCount_ = 0; + setPacketListLoopMode(false, 0, 0); } void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats) { - // Trigger a new sendQ alloc on next packet - currentSendQueue_ = NULL; - currentPacketSetQIdx_ = sendQueueList_.size(); - currentPacketSetSize_ = size; - currentPacketSetRepeat_ = repeats; - currentPacketSetCount_ = 0; + currentPacketSequence_ = new PacketSequence; + currentPacketSequence_->repeatCount_ = repeats; + + repeatSequenceStart_ = packetSequenceList_.size(); + repeatSize_ = size; + packetCount_ = 0; + + packetSequenceList_.append(currentPacketSequence_); } bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, @@ -325,48 +323,51 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, pktHdr.ts.tv_sec = sec; pktHdr.ts.tv_usec = nsec/1000; - if ((currentSendQueue_ == NULL) || - (currentSendQueue_->len + 2*sizeof(pcap_pkthdr) + length) - > currentSendQueue_->maxlen) + if (currentPacketSequence_ == NULL || + !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) { // Add a zero len packet at end of currentSendQueue_ for // inter-sendQ timing - if (sendQueueList_.size()) + if (packetSequenceList_.size()) { pcap_pkthdr hdr = pktHdr; hdr.caplen = 0; - pcap_sendqueue_queue(sendQueueList_.last(), &hdr, (u_char*)packet); + pcap_sendqueue_queue(packetSequenceList_.last()->sendQueue_, + &hdr, (u_char*)packet); } //! \todo (LOW): calculate sendqueue size - currentSendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + currentPacketSequence_ = new PacketSequence; - sendQueueList_.append(currentSendQueue_); - sendQueueRepeat_.append(1); - sendQueueGoto_.append(-1); + packetSequenceList_.append(currentPacketSequence_); // Validate that the pkt will fit inside the new currentSendQueue_ - Q_ASSERT((length + sizeof(pcap_pkthdr)) < currentSendQueue_->maxlen); + Q_ASSERT(currentPacketSequence_->hasFreeSpace( + sizeof(pcap_pkthdr) + length)); } - if (pcap_sendqueue_queue(currentSendQueue_, &pktHdr, (u_char*) packet) < 0) - op = false; - - currentPacketSetCount_++; - if (currentPacketSetCount_ == currentPacketSetSize_) + if (pcap_sendqueue_queue(currentPacketSequence_->sendQueue_, &pktHdr, + (u_char*) packet) < 0) { - qDebug("i=%d, currentPacketSetQIdx_ = %d, currentPacketSetRepeat_ = %d", - sendQueueRepeat_.size(), - currentPacketSetQIdx_, - currentPacketSetRepeat_); - // End current sendQ - sendQueueGoto_.last() = currentPacketSetQIdx_; - sendQueueRepeat_.last() = currentPacketSetRepeat_; - currentPacketSetSize_ = -1; - - // Trigger a new sendQ allocation - currentSendQueue_ = NULL; + op = false; } + packetCount_++; + if (repeatSize_ > 0 && packetCount_ == repeatSize_) + { + qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", + repeatSequenceStart_, repeatSize_); + + // Set the packetSequence repeatSize + Q_ASSERT(repeatSequenceStart_ >= 0); + Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); + packetSequenceList_[repeatSequenceStart_]->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + + repeatSize_ = 0; + + // End current pktSeq and trigger a new pktSeq allocation for next pkt + currentPacketSequence_ = NULL; + } return op; } @@ -407,44 +408,39 @@ void PcapPort::PortTransmitter::run() int i; long overHead = 0; - qDebug("sendQueueList_.size = %d", sendQueueList_.size()); - if (sendQueueList_.size() <= 0) + qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); + if (packetSequenceList_.size() <= 0) return; - for(i = 0; i < sendQueueList_.size(); i++) { - qDebug("sendQ[%d]: rpt = %d, goto = %d", i, - sendQueueRepeat_.at(i), sendQueueGoto_.at(i)); + for(i = 0; i < packetSequenceList_.size(); i++) { + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d", i, + packetSequenceList_.at(i)->repeatCount_, + packetSequenceList_.at(i)->repeatSize_); } - for(i = 0; i < sendQueueList_.size(); i++) + for(i = 0; i < packetSequenceList_.size(); i++) { - int ret; _restart: - int idx = sendQueueGoto_.at(i); - int rpt = sendQueueRepeat_.at(i); - int sqi = i; + int rptSz = packetSequenceList_.at(i)->repeatSize_; + int rptCnt = packetSequenceList_.at(i)->repeatCount_; - for (int n = 0; n < rpt; n++) + for (int j = 0; j < rptCnt; j++) { - Q_ASSERT(sqi <= i); - -#if 0 - if ((n & 0x3F) == 0) - qDebug("i = %d, n = %d/%d, sqi = %d\n", i, n, rpt, sqi); -#endif - ret = sendQueueTransmit(handle_, sendQueueList_.at(sqi), overHead, - kSyncTransmit); - - if (ret < 0) + for (int k = 0; k < rptSz; k++) { - qDebug("error %d in sendQueueTransmit()", ret); - qDebug("overHead = %ld", overHead); - stop_ = false; - return; - } + int ret = sendQueueTransmit(handle_, + packetSequenceList_.at(i+k)->sendQueue_, + overHead, kSyncTransmit); - sqi = (sqi == i) ? idx : sqi+1; + if (ret < 0) + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + return; + } + } } } diff --git a/server/pcapport.h b/server/pcapport.h index daf1507..f046670 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -111,21 +111,43 @@ protected: void run(); void stop(); private: + + class PacketSequence + { + public: + PacketSequence() { + sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + repeatCount_ = 1; + repeatSize_ = 1; + } + ~PacketSequence() { + pcap_sendqueue_destroy(sendQueue_); + } + bool hasFreeSpace(int size) { + if ((sendQueue_->len + size) <= sendQueue_->maxlen) + return true; + else + return false; + } + pcap_send_queue *sendQueue_; + int repeatCount_; + int repeatSize_; + }; + void udelay(long usec); int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, int sync); quint64 ticksFreq_; - int currentPacketSetQIdx_; - int currentPacketSetSize_; - int currentPacketSetRepeat_; - int currentPacketSetCount_; - pcap_send_queue* currentSendQueue_; - QList sendQueueList_; - QList sendQueueRepeat_; - QList sendQueueGoto_; + QList packetSequenceList_; + PacketSequence *currentPacketSequence_; + int repeatSequenceStart_; + quint64 repeatSize_; + quint64 packetCount_; + int returnToQIdx_; long loopDelay_; + bool usingInternalStats_; AbstractPort::PortStats *stats_; bool usingInternalHandle_; From 0223f39994451f034073f70dbd0ddb3671519a61 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 8 Oct 2011 15:55:57 +0530 Subject: [PATCH 179/294] refactored sendQueue code --- common/abstractprotocol.cpp | 12 ++++- server/abstractport.cpp | 91 +++++++++++++++++++++++-------------- server/abstractport.h | 3 +- server/pcapport.cpp | 38 +++++++++++++--- server/pcapport.h | 11 +++-- 5 files changed, 110 insertions(+), 45 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 8ff9529..333bd51 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -882,9 +882,19 @@ quint64 AbstractProtocol::gcd(quint64 u, quint64 v) quint64 AbstractProtocol::lcm(quint64 u, quint64 v) { - /* FIXME: LCM(0,x) := x */ +#if 0 + /* LCM(0,x) := x */ if (u == 0 || v == 0) return u | v; +#else + /* For our use case, neither u nor v can ever be 0, the minimum + value is 1; we do this correction silently here */ + if (u == 0) u = 1; + if (v == 0) v = 1; + + if (u == 1 || v == 1) + return (u * v); +#endif return (u * v)/gcd(u, v); } diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 3bf9148..e186227 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -152,14 +152,18 @@ void AbstractPort::updatePacketListSequential() { if (streamList_[i]->isEnabled()) { - long n, x, y; - long burstSize; + int len; + ulong n, x, y; + ulong burstSize; double ibg = 0; - long ibg1 = 0, ibg2 = 0; - long nb1 = 0, nb2 = 0; + quint64 ibg1 = 0, ibg2 = 0; + quint64 nb1 = 0, nb2 = 0; double ipg = 0; - long ipg1 = 0, ipg2 = 0; - long np1 = 0, np2 = 0; + quint64 ipg1 = 0, ipg2 = 0; + quint64 npx1 = 0, npx2 = 0; + quint64 npy1 = 0, npy2 = 0; + quint64 loopDelay; + ulong frameVariableCount = streamList_[i]->frameVariableCount(); // We derive n, x, y such that // n * x + y = total number of packets to be sent @@ -168,32 +172,37 @@ void AbstractPort::updatePacketListSequential() { case OstProto::StreamControl::e_su_bursts: burstSize = streamList_[i]->burstSize(); - x = AbstractProtocol::lcm(streamList_[i]->frameVariableCount(), - burstSize); - n = ulong(burstSize * streamList_[i]->burstRate()) / x; - y = ulong(burstSize * streamList_[i]->burstRate()) % x; + x = AbstractProtocol::lcm(frameVariableCount, burstSize); + n = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) / x; + y = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) % x; if (streamList_[i]->burstRate() > 0) { ibg = 1e9/double(streamList_[i]->burstRate()); - ibg1 = long(ceil(ibg)); - ibg2 = long(floor(ibg)); - nb1 = long((ibg - double(ibg2)) * double(x)); + ibg1 = quint64(ceil(ibg)); + ibg2 = quint64(floor(ibg)); + nb1 = quint64((ibg - double(ibg2)) * double(x)); nb2 = x - nb1; } + loopDelay = ibg2; break; case OstProto::StreamControl::e_su_packets: - x = streamList_[i]->frameVariableCount(); + x = frameVariableCount; n = streamList_[i]->numPackets() / x; y = streamList_[i]->numPackets() % x; burstSize = x + y; if (streamList_[i]->packetRate() > 0) { ipg = 1e9/double(streamList_[i]->packetRate()); - ipg1 = long(ceil(ipg)); - ipg2 = long(floor(ipg)); - np1 = long((ipg - double(ipg2)) * double(x)); - np2 = x - np1; + ipg1 = quint64(ceil(ipg)); + ipg2 = quint64(floor(ipg)); + npx1 = quint64((ipg - double(ipg2)) * double(x)); + npx2 = x - npx1; + npy1 = quint64((ipg - double(ipg2)) * double(y)); + npy2 = y - npy1; } + loopDelay = ipg2; break; default: qWarning("Unhandled stream control unit %d", @@ -201,25 +210,37 @@ void AbstractPort::updatePacketListSequential() continue; } - qDebug("\nframeVariableCount = %d", - streamList_[i]->frameVariableCount()); - qDebug("n = %ld, x = %ld, y = %ld, burstSize = %ld", + qDebug("\nframeVariableCount = %lu", frameVariableCount); + qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", n, x, y, burstSize); - qDebug("ibg = %g, ibg1/nb1 = %ld/%ld ibg2/nb2 = %ld/%ld", - ibg, ibg1, nb1, ibg2, nb2); - qDebug("ipg = %g, ipg1/np1 = %ld/%ld ipg2/np2 = %ld/%ld\n", - ipg, ipg1, np1, ipg2, np2); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, ibg1); + qDebug("nb1 = %" PRIu64, nb1); + qDebug("ibg2 = %" PRIu64, ibg2); + qDebug("nb2 = %" PRIu64 "\n", nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, ipg1); + qDebug("npx1 = %" PRIu64, npx1); + qDebug("npy1 = %" PRIu64, npy1); + qDebug("ipg2 = %" PRIu64, ipg2); + qDebug("npx2 = %" PRIu64, npx2); + qDebug("npy2 = %" PRIu64 "\n", npy2); if (n > 1) - loopNextPacketSet(x, n); + loopNextPacketSet(x, n, 0, loopDelay); else if (n == 0) x = 0; - for (int j = 0; j < (x+y); j++) + for (uint j = 0; j < (x+y); j++) { - int len; - len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), j); + if (j == 0 || frameVariableCount > 1) + { + len = streamList_[i]->frameValue( + pktBuf_, sizeof(pktBuf_), j); + } if (len <= 0) continue; @@ -228,10 +249,10 @@ void AbstractPort::updatePacketListSequential() appendToPacketList(sec, nsec, pktBuf_, len); - if ((j % burstSize) == 0) + if ((j > 0) && (((j+1) % burstSize) == 0)) { nsec += (j < nb1) ? ibg1 : ibg2; - while (nsec >= 1e9) + while (nsec >= long(1e9)) { sec++; nsec -= long(1e9); @@ -239,8 +260,12 @@ void AbstractPort::updatePacketListSequential() } else { - nsec += (j < np1) ? ipg1 : ipg2; - while (nsec >= 1e9) + if (j < x) + nsec += (j < npx1) ? ipg1 : ipg2; + else + nsec += ((j-x) < npy1) ? ipg1 : ipg2; + + while (nsec >= long(1e9)) { sec++; nsec -= long(1e9); diff --git a/server/abstractport.h b/server/abstractport.h index 3d0f3ce..f848f17 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -71,7 +71,8 @@ public: void setDirty() { isSendQueueDirty_ = true; } virtual void clearPacketList() = 0; - virtual void loopNextPacketSet(qint64 size, qint64 repeats) = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) = 0; virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; virtual void setPacketListLoopMode(bool loop, diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 8a82b1c..3f38ed2 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -301,10 +301,13 @@ void PcapPort::PortTransmitter::clearPacketList() setPacketListLoopMode(false, 0, 0); } -void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats) +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { currentPacketSequence_ = new PacketSequence; currentPacketSequence_->repeatCount_ = repeats; + currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + + repeatDelayNsec/1000; repeatSequenceStart_ = packetSequenceList_.size(); repeatSize_ = size; @@ -328,7 +331,7 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, { // Add a zero len packet at end of currentSendQueue_ for // inter-sendQ timing - if (packetSequenceList_.size()) + if (currentPacketSequence_ != NULL) { pcap_pkthdr hdr = pktHdr; hdr.caplen = 0; @@ -360,8 +363,16 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, // Set the packetSequence repeatSize Q_ASSERT(repeatSequenceStart_ >= 0); Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); - packetSequenceList_[repeatSequenceStart_]->repeatSize_ = - packetSequenceList_.size() - repeatSequenceStart_; + + if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) + { + PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; + + currentPacketSequence_->usecDelay_ = start->usecDelay_; + start->usecDelay_ = 0; + start->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + } repeatSize_ = 0; @@ -413,9 +424,10 @@ void PcapPort::PortTransmitter::run() return; for(i = 0; i < packetSequenceList_.size(); i++) { - qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d", i, + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d usecDelay = %ld", i, packetSequenceList_.at(i)->repeatCount_, - packetSequenceList_.at(i)->repeatSize_); + packetSequenceList_.at(i)->repeatSize_, + packetSequenceList_.at(i)->usecDelay_); } for(i = 0; i < packetSequenceList_.size(); i++) @@ -433,7 +445,19 @@ _restart: packetSequenceList_.at(i+k)->sendQueue_, overHead, kSyncTransmit); - if (ret < 0) + if (ret >= 0) + { + long usecs = packetSequenceList_.at(i+k)->usecDelay_ + + overHead; + if (usecs) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + } + else { qDebug("error %d in sendQueueTransmit()", ret); qDebug("overHead = %ld", overHead); diff --git a/server/pcapport.h b/server/pcapport.h index f046670..30f10bf 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -42,8 +42,10 @@ public: transmitter_->clearPacketList(); setPacketListLoopMode(false, 0, 0); } - virtual void loopNextPacketSet(qint64 size, qint64 repeats) { - transmitter_->loopNextPacketSet(size, repeats); + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { + transmitter_->loopNextPacketSet(size, repeats, + repeatDelaySec, repeatDelayNsec); } virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) { @@ -99,7 +101,8 @@ protected: PortTransmitter(const char *device); ~PortTransmitter(); void clearPacketList(); - void loopNextPacketSet(qint64 size, qint64 repeats); + void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) { @@ -119,6 +122,7 @@ protected: sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); repeatCount_ = 1; repeatSize_ = 1; + usecDelay_ = 0; } ~PacketSequence() { pcap_sendqueue_destroy(sendQueue_); @@ -132,6 +136,7 @@ protected: pcap_send_queue *sendQueue_; int repeatCount_; int repeatSize_; + long usecDelay_; }; void udelay(long usec); From 210bdf11a99246b0fa803c19e8f23823e76d6c54 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 9 Oct 2011 17:57:02 +0530 Subject: [PATCH 180/294] Implemented new method protocolFrameVariableCount() and associated code for all the protocols that require it. With this commit, the set of changes required to reduce the time taken to prebuild the packets for transmit is done. Fixes issue 35 Fixes issue 49 --- common/abstractprotocol.cpp | 29 +++++++++++++++++++- common/abstractprotocol.h | 1 + common/arp.cpp | 53 ++++++++++++++++++++++++++++++++----- common/arp.h | 1 + common/comboprotocol.h | 6 +++++ common/dot3.cpp | 5 ++++ common/dot3.h | 1 + common/gmp.cpp | 19 +++++++++++++ common/gmp.h | 1 + common/ip6.cpp | 13 +++++++++ common/ip6.h | 1 + common/mac.cpp | 12 +++++++++ common/mac.h | 1 + common/payload.cpp | 26 ++++++++++++++++++ common/payload.h | 1 + common/sample.cpp | 12 +++++++++ common/sample.h | 1 + common/tcp.cpp | 8 ++++++ common/tcp.h | 1 + common/udp.cpp | 8 ++++++ common/udp.h | 1 + common/userscript.cpp | 21 +++++++++++++++ common/userscript.h | 15 ++++++++--- server/pcapport.cpp | 2 ++ 24 files changed, 228 insertions(+), 11 deletions(-) diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 333bd51..77def8f 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -57,6 +57,7 @@ along with this program. If not, see - protocolFrameSize() - isProtocolFrameValueVariable() - isProtocolFrameSizeVariable() + - protocolFrameVariableCount() See the description of the methods for more information. @@ -554,7 +555,7 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) */ bool AbstractProtocol::isProtocolFrameValueVariable() const { - return false; + return (protocolFrameVariableCount() > 1); } /*! @@ -633,6 +634,32 @@ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const return false; } +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +int AbstractProtocol::protocolFramePayloadVariableCount() const +{ + int count = 1; + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable() + || p->isProtocolFrameSizeVariable()) + count = lcm(count, p->protocolFrameVariableCount()); + p = p->next; + } + if (parent && (parent->isProtocolFramePayloadValueVariable() + || parent->isProtocolFramePayloadSizeVariable())) + count = lcm(count, parent->protocolFramePayloadVariableCount()); + + return false; +} + /*! Returns true if the protocol typically contains a payload or other protocols following it e.g. TCP, UDP have payloads, while ARP, IGMP do not diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index eb6dd81..ab3b7e9 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -142,6 +142,7 @@ public: virtual int protocolFrameVariableCount() const; bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; bool protocolHasPayload() const; diff --git a/common/arp.cpp b/common/arp.cpp index f23b6b6..84b10a8 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -835,15 +835,54 @@ _exit: return isOk; } -/*! - If your protocol has any variable fields, return true \n - - Otherwise you don't need to reimplement this method - the base class always - returns false -*/ bool ArpProtocol::isProtocolFrameValueVariable() const { - return true; + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + return true; + + return false; +} + +int ArpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); + } + + return count; } QWidget* ArpProtocol::configWidget() diff --git a/common/arp.h b/common/arp.h index 677b73a..d5582d2 100644 --- a/common/arp.h +++ b/common/arp.h @@ -111,6 +111,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/comboprotocol.h b/common/comboprotocol.h index 29cf71d..bc148d3 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -169,6 +169,12 @@ public: return (protoA->isProtocolFrameSizeVariable() || protoB->isProtocolFrameSizeVariable()); } + virtual int protocolFrameVariableCount() const + { + return AbstractProtocol::lcm( + protoA->protocolFrameVariableCount(), + protoB->protocolFrameVariableCount()); + } virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const diff --git a/common/dot3.cpp b/common/dot3.cpp index ea5f6a5..6b7afb0 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -203,6 +203,11 @@ bool Dot3Protocol::isProtocolFrameValueVariable() const return isProtocolFramePayloadSizeVariable(); } +int Dot3Protocol::protocolFrameVariableCount() const +{ + return protocolFramePayloadVariableCount(); +} + QWidget* Dot3Protocol::configWidget() { if (configForm == NULL) diff --git a/common/dot3.h b/common/dot3.h index 403a2ce..22c3f5b 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -70,6 +70,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/gmp.cpp b/common/gmp.cpp index 7bc7ced..a7b4a3d 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -925,6 +925,25 @@ bool GmpProtocol::isProtocolFrameValueVariable() const return false; } +int GmpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return count; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(kGroupCount, FieldValue).toUInt()); + } + + return count; +} + void GmpProtocol::loadConfigWidget() { configWidget(); diff --git a/common/gmp.h b/common/gmp.h index 97dc5fd..7f3fd29 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -80,6 +80,7 @@ public: virtual int protocolFrameSize(int streamIndex = 0) const; virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual void loadConfigWidget(); virtual void storeConfigWidget(); diff --git a/common/ip6.cpp b/common/ip6.cpp index fe5d29b..266c8c2 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -808,6 +808,19 @@ bool Ip6Protocol::isProtocolFrameValueVariable() const return false; } +int Ip6Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + 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) const { diff --git a/common/ip6.h b/common/ip6.h index 1856bc8..dfaf02d 100644 --- a/common/ip6.h +++ b/common/ip6.h @@ -118,6 +118,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; diff --git a/common/mac.cpp b/common/mac.cpp index 040f870..788a10d 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -268,6 +268,18 @@ bool MacProtocol::isProtocolFrameValueVariable() const return false; } +int MacProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.dst_mac_count()); + + if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.src_mac_count()); + + return count; +} QWidget* MacProtocol::configWidget() { diff --git a/common/mac.h b/common/mac.h index 48d0e35..32cd7e2 100644 --- a/common/mac.h +++ b/common/mac.h @@ -81,6 +81,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/payload.cpp b/common/payload.cpp index d5fb2e5..7fd0dd5 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -226,6 +226,32 @@ bool PayloadProtocol::isProtocolFrameSizeVariable() const return true; } +int PayloadProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.pattern_mode() == OstProto::Payload::e_dp_random) + { + switch(mpStream->sendUnit()) + { + case OstProto::StreamControl::e_su_packets: + return mpStream->numPackets(); + + case OstProto::StreamControl::e_su_bursts: + return int(mpStream->numBursts() + * mpStream->burstSize() + * mpStream->burstRate()); + } + } + + if (mpStream->lenMode() != StreamBase::e_fl_fixed) + { + count = AbstractProtocol::lcm(count, + mpStream->frameLenMax() - mpStream->frameLenMin() + 1); + } + + return count; +} QWidget* PayloadProtocol::configWidget() { diff --git a/common/payload.h b/common/payload.h index 2fd32d2..822ee37 100644 --- a/common/payload.h +++ b/common/payload.h @@ -75,6 +75,7 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/sample.cpp b/common/sample.cpp index 2a79660..42f9f32 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -473,6 +473,18 @@ bool SampleProtocol::isProtocolFrameSizeVariable() const return false; } +/*! + TODO: If your protocol frame has any variable fields or has a variable + size, return the minimum number of frames required to vary the fields \n + + Otherwise you don't need to reimplement this method - the base class always + returns 1 +*/ +int SampleProtocol::protocolFrameVariableCount() const +{ + return 1; +} + QWidget* SampleProtocol::configWidget() { /* Lazy creation of the configWidget */ diff --git a/common/sample.h b/common/sample.h index 91e6573..90b9be8 100644 --- a/common/sample.h +++ b/common/sample.h @@ -93,6 +93,7 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/tcp.cpp b/common/tcp.cpp index 02faa6a..c08ca74 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -606,6 +606,14 @@ bool TcpProtocol::isProtocolFrameValueVariable() const return isProtocolFramePayloadValueVariable(); } +int TcpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + QWidget* TcpProtocol::configWidget() { if (configForm == NULL) diff --git a/common/tcp.h b/common/tcp.h index 265937a..9addef0 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -91,6 +91,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/udp.cpp b/common/udp.cpp index 8856751..5a4e14b 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -433,6 +433,14 @@ bool UdpProtocol::isProtocolFrameValueVariable() const return isProtocolFramePayloadValueVariable(); } +int UdpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + QWidget* UdpProtocol::configWidget() { if (configForm == NULL) diff --git a/common/udp.h b/common/udp.h index 623e999..7bdf200 100644 --- a/common/udp.h +++ b/common/udp.h @@ -78,6 +78,7 @@ public: FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; virtual QWidget* configWidget(); virtual void loadConfigWidget(); diff --git a/common/userscript.cpp b/common/userscript.cpp index fa084f1..fece963 100644 --- a/common/userscript.cpp +++ b/common/userscript.cpp @@ -271,6 +271,11 @@ bool UserScriptProtocol::isProtocolFrameSizeVariable() const return userProtocol_.isProtocolFrameSizeVariable(); } +int UserScriptProtocol::protocolFrameVariableCount() const +{ + return userProtocol_.protocolFrameVariableCount(); +} + quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { @@ -532,6 +537,7 @@ void UserProtocol::reset() name_ = QString(); protocolFrameValueVariable_ = false; protocolFrameSizeVariable_ = false; + protocolFrameVariableCount_ = 1; } QString UserProtocol::name() const @@ -564,6 +570,16 @@ void UserProtocol::setProtocolFrameSizeVariable(bool variable) protocolFrameSizeVariable_ = variable; } +int UserProtocol::protocolFrameVariableCount() const +{ + return protocolFrameVariableCount_; +} + +void UserProtocol::setProtocolFrameVariableCount(int count) +{ + protocolFrameVariableCount_ = count; +} + quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const { return parent_->payloadProtocolId( @@ -590,6 +606,11 @@ bool UserProtocol::isProtocolFramePayloadSizeVariable() const return parent_->isProtocolFramePayloadSizeVariable(); } +int UserProtocol::protocolFramePayloadVariableCount() const +{ + return parent_->protocolFramePayloadVariableCount(); +} + quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, AbstractProtocol::CksumType cksumType) const { diff --git a/common/userscript.h b/common/userscript.h index 4ef0d91..99ac226 100644 --- a/common/userscript.h +++ b/common/userscript.h @@ -42,13 +42,17 @@ class UserProtocol : public QObject Q_PROPERTY(bool protocolFrameSizeVariable READ isProtocolFrameSizeVariable WRITE setProtocolFrameSizeVariable); + Q_PROPERTY(int protocolFrameVariableCount + READ protocolFrameVariableCount + WRITE setProtocolFrameVariableCount); public: enum ProtocolIdType { ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, ProtocolIdEth = AbstractProtocol::ProtocolIdEth, - ProtocolIdIp = AbstractProtocol::ProtocolIdIp + ProtocolIdIp = AbstractProtocol::ProtocolIdIp, + ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp }; enum CksumType @@ -70,6 +74,8 @@ public slots: void setProtocolFrameValueVariable(bool variable); bool isProtocolFrameSizeVariable() const; void setProtocolFrameSizeVariable(bool variable); + int protocolFrameVariableCount() const; + void setProtocolFrameVariableCount(int count); quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; int protocolFrameOffset(int streamIndex = 0) const; @@ -77,6 +83,7 @@ public slots: bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; @@ -87,8 +94,9 @@ private: AbstractProtocol *parent_; QString name_; - bool protocolFrameValueVariable_; - bool protocolFrameSizeVariable_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; + int protocolFrameVariableCount_; }; @@ -141,6 +149,7 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 3f38ed2..d31291b 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -298,6 +298,8 @@ void PcapPort::PortTransmitter::clearPacketList() repeatSize_ = 0; packetCount_ = 0; + returnToQIdx_ = -1; + setPacketListLoopMode(false, 0, 0); } From e9ea3d793429ffc4dcef881387a088bbf3c06800 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 12 Oct 2011 20:20:36 +0530 Subject: [PATCH 181/294] Added a minimum size of packets to be looped to improve performance. Also a bug fix that led to incorrect timing between packets --- server/abstractport.cpp | 4 ++++ server/pcapport.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index e186227..715d07d 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -138,6 +138,7 @@ void AbstractPort::updatePacketList() void AbstractPort::updatePacketListSequential() { + const int kMinLoopSize = 16; long sec = 0; long nsec = 0; @@ -189,6 +190,9 @@ void AbstractPort::updatePacketListSequential() break; case OstProto::StreamControl::e_su_packets: x = frameVariableCount; + n = 2; + while (x < kMinLoopSize) + x = frameVariableCount*n++; n = streamList_[i]->numPackets() / x; y = streamList_[i]->numPackets() % x; burstSize = x + y; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index d31291b..5850447 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -451,7 +451,7 @@ _restart: { long usecs = packetSequenceList_.at(i+k)->usecDelay_ + overHead; - if (usecs) + if (usecs > 0) { udelay(usecs); overHead = 0; From 626ca8ad0e09ed1a9f2469ad9701b88e27392b78 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 16 Oct 2011 08:04:09 +0530 Subject: [PATCH 182/294] Code changes to improve transmit performance in Windows. Windows top-speed performance is now at par with Linux in my tests, however for fixed (high) rate transmit, accuracy is not good - need to find ways to improve that. Fixes issue 33 Fixes issue 39 --- server/abstractport.cpp | 4 +- server/abstractport.h | 1 + server/linuxport.cpp | 1 + server/pcapport.cpp | 94 +++++++++++++++++++++++++++++++---------- server/pcapport.h | 23 ++++++++++ server/winpcapport.cpp | 1 + 6 files changed, 99 insertions(+), 25 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 715d07d..8f3997d 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -44,6 +44,7 @@ AbstractPort::AbstractPort(int id, const char *device) isSendQueueDirty_ = false; linkState_ = OstProto::LinkStateUnknown; + minPacketSetSize_ = 1; memset((void*) &stats_, 0, sizeof(stats_)); resetStats(); @@ -138,7 +139,6 @@ void AbstractPort::updatePacketList() void AbstractPort::updatePacketListSequential() { - const int kMinLoopSize = 16; long sec = 0; long nsec = 0; @@ -191,7 +191,7 @@ void AbstractPort::updatePacketListSequential() case OstProto::StreamControl::e_su_packets: x = frameVariableCount; n = 2; - while (x < kMinLoopSize) + while (x < minPacketSetSize_) x = frameVariableCount*n++; n = streamList_[i]->numPackets() / x; y = streamList_[i]->numPackets() % x; diff --git a/server/abstractport.h b/server/abstractport.h index f848f17..7feba7d 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -98,6 +98,7 @@ protected: bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; + ulong minPacketSetSize_; struct PortStats stats_; //! \todo Need lock for stats access/update diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 8d52cc9..82d9277 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -42,6 +42,7 @@ LinuxPort::LinuxPort(int id, const char *device) monitor_ = new StatsMonitor(); data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 5850447..d959f72 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -49,6 +49,25 @@ static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) return usecs; } +#elif defined(Q_OS_WIN32) +static quint64 gTicksFreq; +typedef LARGE_INTEGER TimeStamp; +static void inline getTimeStamp(TimeStamp* stamp) +{ + QueryPerformanceCounter(stamp); +} + +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + if (end->QuadPart > start->QuadPart) + return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; + else + { + // FIXME: incorrect! what's the max value for this counter before + // it rolls over? + return (start->QuadPart)*long(1e6)/gTicksFreq; + } +} #else typedef int TimeStamp; static void inline getTimeStamp(TimeStamp*) {} @@ -256,7 +275,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) #ifdef Q_OS_WIN32 LARGE_INTEGER freq; if (QueryPerformanceFrequency(&freq)) - ticksFreq_ = freq.QuadPart; + gTicksFreq = ticksFreq_ = freq.QuadPart; else Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", "This Win32 platform does not support performance counter"); @@ -331,15 +350,18 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, if (currentPacketSequence_ == NULL || !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) { - // Add a zero len packet at end of currentSendQueue_ for - // inter-sendQ timing if (currentPacketSequence_ != NULL) { - pcap_pkthdr hdr = pktHdr; - hdr.caplen = 0; - pcap_sendqueue_queue(packetSequenceList_.last()->sendQueue_, - &hdr, (u_char*)packet); + long usecs; + + usecs = (pktHdr.ts.tv_sec + - currentPacketSequence_->lastPacket_->ts.tv_sec) + * long(1e6); + usecs += (pktHdr.ts.tv_usec + - currentPacketSequence_->lastPacket_->ts.tv_usec); + currentPacketSequence_->usecDelay_ = usecs; } + //! \todo (LOW): calculate sendqueue size currentPacketSequence_ = new PacketSequence; @@ -350,8 +372,7 @@ bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, sizeof(pcap_pkthdr) + length)); } - if (pcap_sendqueue_queue(currentPacketSequence_->sendQueue_, &pktHdr, - (u_char*) packet) < 0) + if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) { op = false; } @@ -419,17 +440,20 @@ void PcapPort::PortTransmitter::run() const int kSyncTransmit = 1; int i; - long overHead = 0; + long overHead = 0; // overHead should be negative or zero qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); if (packetSequenceList_.size() <= 0) return; for(i = 0; i < packetSequenceList_.size(); i++) { - qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d usecDelay = %ld", i, + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, packetSequenceList_.at(i)->repeatCount_, packetSequenceList_.at(i)->repeatSize_, packetSequenceList_.at(i)->usecDelay_); + qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, + packetSequenceList_.at(i)->packets_, + packetSequenceList_.at(i)->usecDuration_); } for(i = 0; i < packetSequenceList_.size(); i++) @@ -443,14 +467,41 @@ _restart: { for (int k = 0; k < rptSz; k++) { - int ret = sendQueueTransmit(handle_, - packetSequenceList_.at(i+k)->sendQueue_, + int ret; + PacketSequence *seq = packetSequenceList_.at(i+k); +#ifdef Q_OS_WIN32 + TimeStamp ovrStart, ovrEnd; + + if (seq->usecDuration_ <= long(1e6)) // 1s + { + getTimeStamp(&ovrStart); + ret = pcap_sendqueue_transmit(handle_, + seq->sendQueue_, kSyncTransmit); + if (ret >= 0) + { + stats_->txPkts += seq->packets_; + stats_->txBytes += seq->bytes_; + + getTimeStamp(&ovrEnd); + overHead += seq->usecDuration_ + - udiffTimeStamp(&ovrStart, &ovrEnd); + } + if (stop_) + ret = -2; + } + else + { + ret = sendQueueTransmit(handle_, seq->sendQueue_, overHead, kSyncTransmit); + } +#else + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); +#endif if (ret >= 0) { - long usecs = packetSequenceList_.at(i+k)->usecDelay_ - + overHead; + long usecs = seq->usecDelay_ + overHead; if (usecs > 0) { udelay(usecs); @@ -530,14 +581,11 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, getTimeStamp(&ovrStart); } - // A pktLen of size 0 is used at the end of a sendQueue and before - // the next sendQueue - i.e. for inter sendQueue timing - if(pktLen > 0) - { - pcap_sendpacket(p, pkt, pktLen); - stats_->txPkts++; - stats_->txBytes += pktLen; - } + Q_ASSERT(pktLen > 0); + + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; // Step to the next packet in the buffer hdr = (struct pcap_pkthdr*) (pkt + pktLen); diff --git a/server/pcapport.h b/server/pcapport.h index 30f10bf..ee2e7f2 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -120,6 +120,10 @@ protected: public: PacketSequence() { sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + lastPacket_ = NULL; + packets_ = 0; + bytes_ = 0; + usecDuration_ = 0; repeatCount_ = 1; repeatSize_ = 1; usecDelay_ = 0; @@ -133,7 +137,26 @@ protected: else return false; } + int appendPacket(const struct pcap_pkthdr *pktHeader, + const uchar *pktData) { + if (lastPacket_) + { + usecDuration_ += (pktHeader->ts.tv_sec + - lastPacket_->ts.tv_sec) * long(1e6); + usecDuration_ += (pktHeader->ts.tv_usec + - lastPacket_->ts.tv_usec); + } + packets_++; + bytes_ += pktHeader->caplen; + lastPacket_ = (struct pcap_pkthdr *) + (sendQueue_->buffer + sendQueue_->len); + return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); + } pcap_send_queue *sendQueue_; + struct pcap_pkthdr *lastPacket_; + long packets_; + long bytes_; + ulong usecDuration_; int repeatCount_; int repeatSize_; long usecDelay_; diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 6cdbce8..4911581 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -44,6 +44,7 @@ WinPcapPort::WinPcapPort(int id, const char *device) qFatal("failed to alloc oidData"); data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 256; } WinPcapPort::~WinPcapPort() From e5d2ccaa0fb58f98f9dea84b0db6983aae1a7e27 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 20 Oct 2011 07:13:33 +0530 Subject: [PATCH 183/294] Fixed windows problem where tx packets are looped back as rx packets also. Also fixed an issue with transmit at slow rate (< 0.5pps) Fixes issue 46 --- server/abstractport.h | 2 +- server/pcapport.cpp | 25 +++++++++++++++++++++++-- server/pcapport.h | 6 +++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/server/abstractport.h b/server/abstractport.h index 7feba7d..f531e26 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -76,7 +76,7 @@ public: virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; virtual void setPacketListLoopMode(bool loop, - long secDelay, long nsecDelay) = 0; + quint64 secDelay, quint64 nsecDelay) = 0; void updatePacketList(); virtual void startTransmit() = 0; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index d959f72..465b44c 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -160,23 +160,44 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, { int ret; char errbuf[PCAP_ERRBUF_SIZE] = ""; + bool noLocalCapture; direction_ = direction; isDirectional_ = true; isPromisc_ = true; + noLocalCapture = true; stats_ = stats; + _retry: +#ifdef Q_OS_WIN32 + int flags = 0; + + if (isPromisc_) + flags |= PCAP_OPENFLAG_PROMISCUOUS; + if (noLocalCapture) + flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; + + handle_ = pcap_open(device, 64 /* FIXME */, flags, + 1000 /* ms */, NULL, errbuf); +#else handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), - 1000 /* ms */, errbuf); + 1000 /* ms */, errbuf); +#endif if (handle_ == NULL) { if (isPromisc_ && QString(errbuf).contains("promiscuous")) { - qDebug("%s:can't set promiscuous mode, trying non-promisc", device); + qDebug("Can't set promiscuous mode, trying non-promisc %s", device); isPromisc_ = false; goto _retry; } + else if (noLocalCapture && QString(errbuf).contains("loopback")) + { + qDebug("Can't set no local capture mode %s", device); + noLocalCapture = false; + goto _retry; + } else goto _open_error; } diff --git a/server/pcapport.h b/server/pcapport.h index ee2e7f2..814528d 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -51,7 +51,7 @@ public: int length) { return transmitter_->appendToPacketList(sec, nsec, packet, length); } - virtual void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) + virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); } @@ -105,7 +105,7 @@ protected: long repeatDelaySec, long repeatDelayNsec); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); - void setPacketListLoopMode(bool loop, long secDelay, long nsecDelay) { + void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { returnToQIdx_ = loop ? 0 : -1; loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; } @@ -174,7 +174,7 @@ protected: quint64 packetCount_; int returnToQIdx_; - long loopDelay_; + quint64 loopDelay_; bool usingInternalStats_; AbstractPort::PortStats *stats_; From 264b44641050eb810e46b7961ff4dcbda30781d9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 21 Oct 2011 22:11:38 +0530 Subject: [PATCH 184/294] Replaced ULONG_LONG_MAX with the more portable (and standard) ULLONG_MAX --- server/abstractport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 8f3997d..55e31a3 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -319,7 +319,7 @@ _stop_no_more_pkts: void AbstractPort::updatePacketListInterleaved() { int numStreams = 0; - quint64 minGap = ULONG_LONG_MAX; + quint64 minGap = ULLONG_MAX; quint64 duration = quint64(1e9); QList ibg1, ibg2; QList nb1, nb2; From e188655e674931c58172d7a8c03955545457015b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 22 Oct 2011 14:55:05 +0530 Subject: [PATCH 185/294] For Linux added specific code to put interfaces in PROMISC mode, since we no longer use PCAP to do stats (which was indirectly setting PROMISC). Also added signal handlers to do cleanup for SIGTERM and SIGINT. Some code indentation fixes also. Fixes issue 52 --- server/drone_main.cpp | 22 ++++ server/linuxport.cpp | 281 ++++++++++++++++++++++++++---------------- server/linuxport.h | 1 + 3 files changed, 195 insertions(+), 109 deletions(-) diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 3f16bcc..e59cb48 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -21,10 +21,19 @@ along with this program. If not, see #include "../common/protocolmanager.h" +#ifdef Q_OS_UNIX +#include "signal.h" +#endif + extern ProtocolManager *OstProtocolManager; int myport; +void cleanup(int /*signum*/) +{ + qApp->exit(-1); +} + int main(int argc, char *argv[]) { QApplication app(argc, argv); @@ -39,11 +48,24 @@ int main(int argc, char *argv[]) if (!drone.init()) exit(-1); +#ifdef Q_OS_UNIX + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup; + if (sigaction(SIGTERM, &sa, NULL)) + qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); + if (sigaction(SIGINT, &sa, NULL)) + qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); +#endif + drone.setWindowFlags(drone.windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint); drone.showMinimized(); app.exec(); + + delete OstProtocolManager; + return 0; } diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 82d9277..7e15101 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -23,9 +23,13 @@ along with this program. If not, see #include -#include -#include +#include #include +#include +#include +#include +#include +#include QList LinuxPort::allPorts_; LinuxPort::StatsMonitor *LinuxPort::monitor_; @@ -33,13 +37,16 @@ LinuxPort::StatsMonitor *LinuxPort::monitor_; LinuxPort::LinuxPort(int id, const char *device) : PcapPort(id, device) { + clearPromisc_ = false; + // We don't need per port Rx/Tx monitors for Linux delete monitorRx_; delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; // We have one monitor for both Rx/Tx of all ports if (!monitor_) - monitor_ = new StatsMonitor(); + monitor_ = new StatsMonitor(); data_.set_is_exclusive_control(hasExclusiveControl()); minPacketSetSize_ = 16; @@ -50,6 +57,32 @@ LinuxPort::LinuxPort(int id, const char *device) LinuxPort::~LinuxPort() { + qDebug("In %s", __FUNCTION__); + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if (ifr.ifr_flags & IFF_PROMISC) + { + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + } + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } } void LinuxPort::init() @@ -57,7 +90,7 @@ void LinuxPort::init() // TODO: Update Notes with Promisc/Non-Promisc if (!monitor_->isRunning()) - monitor_->start(); + monitor_->start(); } OstProto::LinkState LinuxPort::linkState() @@ -86,16 +119,17 @@ LinuxPort::StatsMonitor::StatsMonitor() void LinuxPort::StatsMonitor::run() { PortStats **portStats; - int fd; + int fd, sd; QByteArray buf; int len; char *p, *end; int count, index; const char* fmtopt[] = { - "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u\n", - "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u%n\n", + "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u%n\n", }; const char *fmt; + struct ifreq ifr; // // We first setup stuff before we start polling for stats @@ -103,16 +137,16 @@ void LinuxPort::StatsMonitor::run() fd = open("/proc/net/dev", O_RDONLY); if (fd < 0) { - qWarning("Unable to open /proc/net/dev - no stats will be available"); - return; + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; } buf.fill('\0', 8192); len = read(fd, (void*) buf.data(), buf.size()); if (len < 0) { - qWarning("initial buffer size is too small. no stats will be available"); - return; + qWarning("initial buffer size is too small. no stats will be available"); + return; } p = buf.data(); @@ -120,29 +154,33 @@ void LinuxPort::StatsMonitor::run() // Select scanf format if (strstr(buf, "compressed")) - fmt = fmtopt[0]; + fmt = fmtopt[0]; else - fmt = fmtopt[1]; + fmt = fmtopt[1]; // Count number of lines - number of ports is 2 less than number of lines count = 0; while (p < end) { - if (*p == '\n') - count++; - p++; + if (*p == '\n') + count++; + p++; } count -= 2; if (count <= 0) { - qWarning("no ports in /proc/dev/net - no stats will be available"); - return; + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; } portStats = (PortStats**) calloc(count, sizeof(PortStats)); Q_ASSERT(portStats != NULL); + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + // // Populate the port stats array // @@ -150,48 +188,73 @@ void LinuxPort::StatsMonitor::run() // Skip first two lines while (*p != '\n') - p++; + p++; p++; while (*p != '\n') - p++; + p++; p++; index = 0; while (p < end) { - char* q; + char* q; - // Skip whitespace - while ((p < end) && (*p == ' ')) - p++; - - q = p; + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; - // Get interface name - while ((q < end) && (*q != ':') && (*q != '\n')) - q++; + q = p; - if ((q < end) && (*q == ':')) - { - foreach(LinuxPort* port, allPorts_) + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) { - if (strncmp(port->name(), p, int(q-p)) == 0) - { - portStats[index] = &(port->stats_); - break; - } - } - } - index++; + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); - // Skip till newline - p = q; - while (*p != '\n') + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + port->clearPromisc_ = true; + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; p++; - p++; } Q_ASSERT(index == count); + close(sd); + qDebug("stats for %d ports setup", count); // @@ -199,77 +262,77 @@ void LinuxPort::StatsMonitor::run() // while (1) { - lseek(fd, 0, SEEK_SET); - len = read(fd, (void*) buf.data(), buf.size()); - if (len < 0) - { - if (buf.size() > 1*1024*1024) + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) { - qWarning("buffer size hit limit. no more stats"); - return; - } - qDebug("doubling buffer size. curr = %d", buf.size()); - buf.resize(buf.size() * 2); - continue; - } - - p = buf.data(); - end = p + len; - - // Skip first two lines - while (*p != '\n') - p++; - p++; - while (*p != '\n') - p++; - p++; - - index = 0; - while (p < end) - { - uint dummy; - quint64 rxBytes, rxPkts; - quint64 txBytes, txPkts; - - // Skip interface name - we assume the number and order of ports - // won't change since we parsed the output before we started polling - while ((p < end) && (*p != ':') && (*p != '\n')) - p++; - if (p >= end) - break; - if (*p == '\n') - { - index++; - continue; - } - p++; - - sscanf(p, fmt, - &rxBytes, &rxPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, - &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); - - if (index < count) - { - AbstractPort::PortStats *stats = portStats[index]; - if (stats) - { - stats->rxPps = (rxPkts - stats->rxPkts)/kRefreshFreq_; - stats->rxBps = (rxBytes - stats->rxBytes)/kRefreshFreq_; - stats->rxPkts = rxPkts; - stats->rxBytes = rxBytes; - stats->txPps = (txPkts - stats->txPkts)/kRefreshFreq_; - stats->txBps = (txBytes - stats->txBytes)/kRefreshFreq_; - stats->txPkts = txPkts; - stats->txBytes = txBytes; - } + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; } + p = buf.data(); + end = p + len; + + // Skip first two lines while (*p != '\n') + p++; p++; + while (*p != '\n') + p++; p++; - index++; - } - QThread::sleep(kRefreshFreq_); + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + stats->rxPps = (rxPkts - stats->rxPkts)/kRefreshFreq_; + stats->rxBps = (rxBytes - stats->rxBytes)/kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = (txPkts - stats->txPkts)/kRefreshFreq_; + stats->txBps = (txBytes - stats->txBytes)/kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); } } diff --git a/server/linuxport.h b/server/linuxport.h index 8fbedf5..4fe3305 100644 --- a/server/linuxport.h +++ b/server/linuxport.h @@ -48,6 +48,7 @@ protected: static const int kRefreshFreq_ = 1; // in seconds }; + bool clearPromisc_; static QList allPorts_; static StatsMonitor *monitor_; // rx/tx stats for ALL ports }; From 7f0ffd66e0a1a51160f359b23f913db4be932fbe Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 Oct 2011 11:38:13 +0530 Subject: [PATCH 186/294] removed warnings --- client/streamconfigdialog.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 26319ea..e2fc3f0 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -407,7 +407,6 @@ void StreamConfigDialog::on_tbAdd_clicked() { int n = 0; QModelIndex idx2; - AbstractProtocol *p; QModelIndexList selection; selection = lvAllProtocols->selectionModel()->selectedIndexes(); @@ -426,7 +425,7 @@ void StreamConfigDialog::on_tbAdd_clicked() if (!_iter->hasNext()) return; - p = _iter->next(); + _iter->next(); } foreach(QModelIndex idx, selection) @@ -1023,7 +1022,7 @@ void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); } -void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &text) +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) { if (rbSendBursts->isChecked()) on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); From baf709b24d43c0156e3353c6cdbbe3bd7f1436bf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 Oct 2011 13:08:24 +0530 Subject: [PATCH 187/294] Fixed valgrind reported mem leaks --- client/main.cpp | 5 +++++ server/drone_main.cpp | 28 +++++++++++++++++++--------- server/linuxport.cpp | 15 ++++++++++++++- server/linuxport.h | 2 ++ server/pcapport.cpp | 2 ++ 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/client/main.cpp b/client/main.cpp index 07b073f..b9f7ae9 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -26,6 +26,8 @@ along with this program. If not, see #include #include +#include + extern const char* version; extern const char* revision; extern ProtocolManager *OstProtocolManager; @@ -76,8 +78,11 @@ int main(int argc, char* argv[]) mainWindow = new MainWindow; mainWindow->show(); exitCode = app.exec(); + delete mainWindow; delete appSettings; + delete OstProtocolManager; + google::protobuf::ShutdownProtobufLibrary(); return exitCode; } diff --git a/server/drone_main.cpp b/server/drone_main.cpp index e59cb48..64763cb 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -21,8 +21,10 @@ along with this program. If not, see #include "../common/protocolmanager.h" +#include + #ifdef Q_OS_UNIX -#include "signal.h" +#include #endif extern ProtocolManager *OstProtocolManager; @@ -36,17 +38,21 @@ void cleanup(int /*signum*/) int main(int argc, char *argv[]) { + int exitCode = 0; QApplication app(argc, argv); - Drone drone; + Drone *drone = new Drone(); OstProtocolManager = new ProtocolManager(); - app.setApplicationName(drone.objectName()); + app.setApplicationName(drone->objectName()); if (argc > 1) myport = atoi(argv[1]); - if (!drone.init()) - exit(-1); + if (!drone->init()) + { + exitCode = -1; + goto _exit; + } #ifdef Q_OS_UNIX struct sigaction sa; @@ -58,14 +64,18 @@ int main(int argc, char *argv[]) qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); #endif - drone.setWindowFlags(drone.windowFlags() + drone->setWindowFlags(drone->windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint); - drone.showMinimized(); - app.exec(); + drone->showMinimized(); + exitCode = app.exec(); +_exit: + delete drone; delete OstProtocolManager; - return 0; + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; } diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 7e15101..12f5cb3 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -59,6 +59,12 @@ LinuxPort::~LinuxPort() { qDebug("In %s", __FUNCTION__); + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + if (clearPromisc_) { int sd = socket(AF_INET, SOCK_DGRAM, 0); @@ -114,6 +120,7 @@ bool LinuxPort::setExclusiveControl(bool /*exclusive*/) LinuxPort::StatsMonitor::StatsMonitor() : QThread() { + stop_ = false; } void LinuxPort::StatsMonitor::run() @@ -260,7 +267,7 @@ void LinuxPort::StatsMonitor::run() // // We are all set - Let's start polling for stats! // - while (1) + while (!stop_) { lseek(fd, 0, SEEK_SET); len = read(fd, (void*) buf.data(), buf.size()); @@ -334,6 +341,12 @@ void LinuxPort::StatsMonitor::run() } QThread::sleep(kRefreshFreq_); } + + free(portStats); } +void LinuxPort::StatsMonitor::stop() +{ + stop_ = true; +} #endif diff --git a/server/linuxport.h b/server/linuxport.h index 4fe3305..817ece1 100644 --- a/server/linuxport.h +++ b/server/linuxport.h @@ -44,8 +44,10 @@ protected: public: StatsMonitor(); void run(); + void stop(); private: static const int kRefreshFreq_ = 1; // in seconds + bool stop_; }; bool clearPromisc_; diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 465b44c..cefffb3 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -324,6 +324,8 @@ PcapPort::PortTransmitter::~PortTransmitter() { if (usingInternalStats_) delete stats_; + if (usingInternalHandle_) + pcap_close(handle_); } void PcapPort::PortTransmitter::clearPacketList() From 763219e153d71e1d18a61c3e5dc5cebbbf4ff721 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 Oct 2011 17:39:15 +0530 Subject: [PATCH 188/294] Integrated newer version (0.6.1) of the QHexEdit Widget from http://qhexedit2.googlecode.com. This newer version supports copy-paste and undo-redo. --- extra/qhexedit2/qhexedit2.pro | 12 +- extra/qhexedit2/src/commands.cpp | 115 +++++ extra/qhexedit2/src/commands.h | 70 +++ extra/qhexedit2/src/qhexedit.cpp | 50 ++- extra/qhexedit2/src/qhexedit.h | 94 +++- extra/qhexedit2/src/qhexedit_p.cpp | 672 +++++++++++++++++++++++------ extra/qhexedit2/src/qhexedit_p.h | 76 +++- extra/qhexedit2/src/xbytearray.cpp | 167 +++++++ extra/qhexedit2/src/xbytearray.h | 66 +++ 9 files changed, 1137 insertions(+), 185 deletions(-) create mode 100644 extra/qhexedit2/src/commands.cpp create mode 100644 extra/qhexedit2/src/commands.h create mode 100644 extra/qhexedit2/src/xbytearray.cpp create mode 100644 extra/qhexedit2/src/xbytearray.h diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro index c7b9989..89479e7 100644 --- a/extra/qhexedit2/qhexedit2.pro +++ b/extra/qhexedit2/qhexedit2.pro @@ -1,8 +1,12 @@ TEMPLATE = lib CONFIG += qt staticlib warn_on -HEADERS = src/qhexedit.h \ - src/qhexedit_p.h +HEADERS = src/commands.h\ + src/qhexedit.h \ + src/qhexedit_p.h \ + src/xbytearray.h -SOURCES = src/qhexedit.cpp \ - src/qhexedit_p.cpp +SOURCES = src/commands.cpp \ + src/qhexedit.cpp \ + src/qhexedit_p.cpp \ + src/xbytearray.cpp diff --git a/extra/qhexedit2/src/commands.cpp b/extra/qhexedit2/src/commands.cpp new file mode 100644 index 0000000..303091d --- /dev/null +++ b/extra/qhexedit2/src/commands.cpp @@ -0,0 +1,115 @@ +#include "commands.h" + +CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _xData = xData; + _charPos = charPos; + _newChar = newChar; + _cmd = cmd; +} + +bool CharCommand::mergeWith(const QUndoCommand *command) +{ + const CharCommand *nextCommand = static_cast(command); + bool result = false; + + if (_cmd != remove) + { + if (nextCommand->_cmd == replace) + if (nextCommand->_charPos == _charPos) + { + _newChar = nextCommand->_newChar; + result = true; + } + } + return result; +} + +void CharCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_charPos, 1); + break; + case replace: + _xData->replace(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + case remove: + _xData->insert(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + } +} + +void CharCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_charPos, _newChar); + break; + case replace: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->replace(_charPos, _newChar); + break; + case remove: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->remove(_charPos, 1); + break; + } +} + + + +ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _cmd = cmd; + _xData = xData; + _baPos = baPos; + _newBa = newBa; + _len = len; +} + +void ArrayCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_baPos, _newBa.length()); + break; + case replace: + _xData->replace(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + case remove: + _xData->insert(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + } +} + +void ArrayCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_baPos, _newBa); + break; + case replace: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->replace(_baPos, _newBa); + break; + case remove: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->remove(_baPos, _len); + break; + } +} diff --git a/extra/qhexedit2/src/commands.h b/extra/qhexedit2/src/commands.h new file mode 100644 index 0000000..9931b3f --- /dev/null +++ b/extra/qhexedit2/src/commands.h @@ -0,0 +1,70 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +/** \cond docNever */ + +#include + +#include "xbytearray.h" + +/*! CharCommand is a class to prived undo/redo functionality in QHexEdit. +A QUndoCommand represents a single editing action on a document. CharCommand +is responsable for manipulations on single chars. It can insert. replace and +remove characters. A manipulation stores allways to actions +1. redo (or do) action +2. undo action. + +CharCommand also supports command compression via mergeWidht(). This allows +the user to execute a undo command contation e.g. 3 steps in a single command. +If you for example insert a new byt "34" this means for the editor doing 3 +steps: insert a "00", replace it with "03" and the replace it with "34". These +3 steps are combined into a single step, insert a "34". +*/ +class CharCommand : public QUndoCommand +{ +public: + enum { Id = 1234 }; + enum Cmd {insert, remove, replace}; + + CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, + QUndoCommand *parent=0); + + void undo(); + void redo(); + bool mergeWith(const QUndoCommand *command); + int id() const { return Id; } + +private: + XByteArray * _xData; + int _charPos; + bool _wasChanged; + char _newChar; + char _oldChar; + Cmd _cmd; +}; + +/*! ArrayCommand provides undo/redo functionality for handling binary strings. It +can undo/redo insert, replace and remove binary strins (QByteArrays). +*/ +class ArrayCommand : public QUndoCommand +{ +public: + enum Cmd {insert, remove, replace}; + ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, + QUndoCommand *parent=0); + void undo(); + void redo(); + +private: + Cmd _cmd; + XByteArray * _xData; + int _baPos; + int _len; + QByteArray _wasChanged; + QByteArray _newBa; + QByteArray _oldBa; +}; + +/** \endcond docNever */ + +#endif // COMMANDS_H diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp index a295110..b6dd38d 100644 --- a/extra/qhexedit2/src/qhexedit.cpp +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -9,9 +9,11 @@ QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) setWidget(qHexEdit_p); setWidgetResizable(true); + connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); + connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); - connect(qHexEdit_p, SIGNAL(currentAddress(int)), this, SIGNAL(currentAddress(int))); connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); + setFocusPolicy(Qt::NoFocus); } void QHexEdit::insert(int i, const QByteArray & ba) @@ -29,11 +31,31 @@ void QHexEdit::remove(int pos, int len) qHexEdit_p->remove(pos, len); } +QString QHexEdit::toReadableString() +{ + return qHexEdit_p->toRedableString(); +} + +QString QHexEdit::selectionToReadableString() +{ + return qHexEdit_p->selectionToReadableString(); +} + void QHexEdit::setAddressArea(bool addressArea) { qHexEdit_p->setAddressArea(addressArea); } +void QHexEdit::redo() +{ + qHexEdit_p->redo(); +} + +void QHexEdit::undo() +{ + qHexEdit_p->undo(); +} + void QHexEdit::setAddressWidth(int addressWidth) { qHexEdit_p->setAddressWidth(addressWidth); @@ -56,7 +78,7 @@ void QHexEdit::setAddressOffset(int offset) int QHexEdit::addressOffset() { - return addressOffset(); + return qHexEdit_p->addressOffset(); } void QHexEdit::setData(const QByteArray &data) @@ -89,6 +111,16 @@ QColor QHexEdit::highlightingColor() return qHexEdit_p->highlightingColor(); } +void QHexEdit::setSelectionColor(const QColor &color) +{ + qHexEdit_p->setSelectionColor(color); +} + +QColor QHexEdit::selectionColor() +{ + return qHexEdit_p->selectionColor(); +} + void QHexEdit::setOverwriteMode(bool overwriteMode) { qHexEdit_p->setOverwriteMode(overwriteMode); @@ -99,8 +131,22 @@ bool QHexEdit::overwriteMode() return qHexEdit_p->overwriteMode(); } +void QHexEdit::setReadOnly(bool readOnly) +{ + qHexEdit_p->setReadOnly(readOnly); +} + +bool QHexEdit::isReadOnly() +{ + return qHexEdit_p->isReadOnly(); +} + void QHexEdit::setFont(const QFont &font) { qHexEdit_p->setFont(font); } +const QFont & QHexEdit::font() const +{ + return qHexEdit_p->font(); +} diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h index 2b94581..b5a9601 100644 --- a/extra/qhexedit2/src/qhexedit.h +++ b/extra/qhexedit2/src/qhexedit.h @@ -7,27 +7,40 @@ /** \mainpage QHexEdit is a binary editor widget for Qt. -\version Version 0.4.3 +\version Version 0.6.1 \image html hexedit.png */ /*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. -It is a simple editor for binary data, just like QPlainTextEdit is for text data. -There are sip configuration files included, so it is easy to create bindings -for PyQt and you can use this widget also in python. +It is a simple editor for binary data, just like QPlainTextEdit is for text +data. There are sip configuration files included, so it is easy to create +bindings for PyQt and you can use this widget also in python. -QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the -mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f) -you will change the data. Changed data is highlighted and can be accessed via data(). +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use +the mouse or the keyboard to navigate inside the widget. If you hit the keys +(0..9, a..f) you will change the data. Changed data is highlighted and can be +accessed via data(). -Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and -insert data. In this case the size of data() increases. It is also possible to delete -bytes under the cursor, here the size of data decreases. +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) +and insert data. In this case the size of data() increases. It is also possible +to delete bytes (del or backspace), here the size of data decreases. -There are some limitations: The size of data has in general to be below 10 megabytes, -otherwise the scroll sliders ard not shown and you can't scroll any more. Copy and -paste functionality is perhaps a subject of a later release. +You can select data with keyboard hits or mouse movements. The copy-key will +copy the selected data into the clipboard. The cut-key copies also but delets +it afterwards. In overwrite mode, the paste function overwrites the content of +the (does not change the length) data. In insert mode, clipboard data will be +inserted. The clipboard content is expected in ASCII Hex notation. Unknown +characters will be ignored. + +QHexEdit comes with undo/redo functionality. All changes can be undone, by +pressing the undo-key (usually ctr-z). They can also be redone afterwards. +The undo/redo framework is cleared, when setData() sets up a new +content for the editor. + +This widget can only handle small amounts of data. The size has to be below 10 +megabytes, otherwise the scroll sliders ard not shown and you can't scroll any +more. */ class QHexEdit : public QScrollArea { @@ -55,11 +68,30 @@ paste functionality is perhaps a subject of a later release. */ Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + /*! Property selection color sets (setSelectionColor()) the backgorund + color of selected text areas. You can also read the color + (selectionColor()). + */ + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode - in which the editor works. In overwritem mode the user will overwrite existing data. + in which the editor works. In overwrite mode the user will overwrite existing data. The + size of data will be constant. In insert mode the size will grow, when inserting + new data. */ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode + in which the editor works. In readonly mode the the user can only navigate + through the data and select data; modifying is not possible. This + property's default is false. + */ + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + Q_PROPERTY(QFont font READ font WRITE setFont) + + public: /*! Creates an instance of QHexEdit. \param parent Parent widget of QHexEdit. @@ -69,23 +101,33 @@ public: /*! Inserts a byte array. \param i Index position, where to insert \param ba byte array, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. */ void insert(int i, const QByteArray & ba); /*! Inserts a char. \param i Index position, where to insert \param ch Char, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. */ void insert(int i, char ch); /*! Removes len bytes from the content. \param pos Index position, where to remove \param len Amount of bytes to remove + In overwrite mode, the existing bytes will be overwriten with 0x00. */ void remove(int pos, int len=1); - /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ - void setFont(const QFont &); + /*! Gives back a formatted image of the content of QHexEdit + */ + QString toReadableString(); + + /*! Gives back a formatted image of the selected content of QHexEdit + */ + QString selectionToReadableString(); /*! \cond docNever */ void setAddressOffset(int offset); @@ -96,11 +138,21 @@ public: QColor addressAreaColor(); void setHighlightingColor(QColor const &color); QColor highlightingColor(); + void setSelectionColor(QColor const &color); + QColor selectionColor(); void setOverwriteMode(bool); bool overwriteMode(); + void setReadOnly(bool); + bool isReadOnly(); + const QFont &font() const; + void setFont(const QFont &); /*! \endcond docNever */ public slots: + /*! Redoes the last operation. If there is no operation to redo, i.e. + there is no redo step in the undo/redo history, nothing happens. + */ + void redo(); /*! Set the minimum width of the address area. \param addressWidth Width in characters. @@ -122,10 +174,18 @@ public slots: */ void setHighlighting(bool mode); + /*! Undoes the last operation. If there is no operation to undo, i.e. + there is no undo step in the undo/redo history, nothing happens. + */ + void undo(); + signals: /*! Contains the address, where the cursor is located. */ - void currentAddress(int address); + void currentAddressChanged(int address); + + /*! Contains the size of the data to edit. */ + void currentSizeChanged(int size); /*! The signal is emited every time, the data is changed. */ void dataChanged(); diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp index 30f0660..2f046bb 100644 --- a/extra/qhexedit2/src/qhexedit_p.cpp +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -1,6 +1,7 @@ #include #include "qhexedit_p.h" +#include "commands.h" const int HEXCHARS_IN_LINE = 47; const int GAP_ADR_HEX = 10; @@ -9,6 +10,8 @@ const int BYTES_PER_LINE = 16; QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) { + _undoStack = new QUndoStack(this); + _scrollArea = parent; setAddressWidth(4); setAddressOffset(0); @@ -16,41 +19,44 @@ QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) setAsciiArea(true); setHighlighting(true); setOverwriteMode(true); - setAddressAreaColor(QColor(Qt::lightGray).lighter(110)); - setHighlightingColor(QColor(Qt::yellow).lighter(160)); + setReadOnly(false); + setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); + setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); + setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); + setFont(QFont("Courier", 10)); - setFont(QFont("Mono", 10)); - connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); - - _cursorTimer.setInterval(500); - _cursorTimer.start(); + _size = 0; + resetSelection(0); setFocusPolicy(Qt::StrongFocus); + + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + _cursorTimer.setInterval(500); + _cursorTimer.start(); } void QHexEditPrivate::setAddressOffset(int offset) { - _addressOffset = offset; + _xData.setAddressOffset(offset); adjust(); } int QHexEditPrivate::addressOffset() { - return _addressOffset; + return _xData.addressOffset(); } void QHexEditPrivate::setData(const QByteArray &data) { - _data = data; - _originalData = data; + _xData.setData(data); + _undoStack->clear(); adjust(); setCursorPos(0); - setFocus(); } QByteArray QHexEditPrivate::data() { - return _data; + return _xData.data(); } void QHexEditPrivate::setAddressAreaColor(const QColor &color) @@ -75,54 +81,123 @@ QColor QHexEditPrivate::highlightingColor() return _highlightingColor; } -void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +void QHexEditPrivate::setSelectionColor(const QColor &color) { - if (overwriteMode != _overwriteMode) + _selectionColor = color; + update(); +} + +QColor QHexEditPrivate::selectionColor() +{ + return _selectionColor; +} + +void QHexEditPrivate::setReadOnly(bool readOnly) +{ + _readOnly = readOnly; +} + +bool QHexEditPrivate::isReadOnly() +{ + return _readOnly; +} + +XByteArray & QHexEditPrivate::xData() +{ + return _xData; +} + +void QHexEditPrivate::insert(int index, const QByteArray & ba) +{ + if (ba.length() > 0) { - emit overwriteModeChanged(overwriteMode); - _overwriteMode = overwriteMode; - adjust(); + if (_overwriteMode) + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } } } -bool QHexEditPrivate::overwriteMode() +void QHexEditPrivate::insert(int index, char ch) { - return _overwriteMode; -} - -void QHexEditPrivate::insert(int i, const QByteArray & ba) -{ - _data.insert(i, ba); - _originalData.insert(i, ba); -} - -void QHexEditPrivate::insert(int i, char ch) -{ - _data.insert(i, ch); - _originalData.insert(i, ch); + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); } void QHexEditPrivate::remove(int index, int len) { - _data.remove(index, len); - _originalData.remove(index, len); + if (len > 0) + { + if (len == 1) + { + if (_overwriteMode) + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + else + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + } + else + { + QByteArray ba = QByteArray(len, char(0)); + if (_overwriteMode) + { + QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } + } +} + +void QHexEditPrivate::replace(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::replace(int index, const QByteArray & ba) +{ + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); } void QHexEditPrivate::setAddressArea(bool addressArea) { _addressArea = addressArea; adjust(); + setCursorPos(_cursorPosition); } void QHexEditPrivate::setAddressWidth(int addressWidth) { - if ((addressWidth >= 0) and (addressWidth<=6)) - { - _addressNumbers = addressWidth; - adjust(); - setCursorPos(_cursorPosition); - } + _xData.setAddressWidth(addressWidth); + + setCursorPos(_cursorPosition); } void QHexEditPrivate::setAsciiArea(bool asciiArea) @@ -143,95 +218,340 @@ void QHexEditPrivate::setHighlighting(bool mode) update(); } +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + _overwriteMode = overwriteMode; +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::redo() +{ + _undoStack->redo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +void QHexEditPrivate::undo() +{ + _undoStack->undo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +QString QHexEditPrivate::toRedableString() +{ + return _xData.toRedableString(); +} + + +QString QHexEditPrivate::selectionToReadableString() +{ + return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); +} + void QHexEditPrivate::keyPressEvent(QKeyEvent *event) { - bool down = false; int charX = (_cursorX - _xPosHex) / _charWidth; int posX = (charX / 3) * 2 + (charX % 3); int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; - int key = int(event->text()[0].toAscii()); - if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) - { - // calc address - - // insert char - if (_overwriteMode == false) - if ((charX % 3) == 0) - { - insert(posBa, char(0)); - adjust(); - } - QByteArray hexValue = _data.mid(posBa, 1).toHex(); - if ((charX % 3) == 0) - hexValue[0] = key; - else - hexValue[1] = key; - _data.replace(posBa, 1, QByteArray().fromHex(hexValue)); - emit dataChanged(); - - setCursorPos(_cursorPosition + 1); - down = true; - } - - // delete char - if (event->matches(QKeySequence::Delete)) - remove(posBa); - if (event->key() == Qt::Key_Backspace) - { - remove(posBa - 1); - setCursorPos(_cursorPosition - 2); - } - - // handle other function keys - if (event->key() == Qt::Key_Insert) - setOverwriteMode(!_overwriteMode); +/*****************************************************************************/ +/* Cursor movements */ +/*****************************************************************************/ if (event->matches(QKeySequence::MoveToNextChar)) { setCursorPos(_cursorPosition + 1); - down = true; + resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousChar)) - setCursorPos(_cursorPosition - 1); - if (event->matches(QKeySequence::MoveToStartOfLine)) - setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + { + setCursorPos(_cursorPosition - 1); + resetSelection(_cursorPosition); + } if (event->matches(QKeySequence::MoveToEndOfLine)) - setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + { + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfLine)) + { + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + resetSelection(_cursorPosition); + } if (event->matches(QKeySequence::MoveToPreviousLine)) - setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); - if (event->matches(QKeySequence::MoveToPreviousPage)) - setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); - if (event->matches(QKeySequence::MoveToStartOfDocument)) - setCursorPos(0); + { + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } if (event->matches(QKeySequence::MoveToNextLine)) { setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); - down = true; - } - if (event->matches(QKeySequence::MoveToEndOfDocument)) - { - setCursorPos(_data.size() * 2); - down = true; + resetSelection(_cursorPosition); } + if (event->matches(QKeySequence::MoveToNextPage)) { setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); - down = true; + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousPage)) + { + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_xData.size() * 2); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfDocument)) + { + setCursorPos(0); + resetSelection(_cursorPosition); } - // when we move downwards, we have to go a little further - if (down) - _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3 + _charHeight); - else - _scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3); +/*****************************************************************************/ +/* Select commands */ +/*****************************************************************************/ + if (event->matches(QKeySequence::SelectAll)) + { + resetSelection(0); + setSelection(2*_xData.size() + 1); + } + if (event->matches(QKeySequence::SelectNextChar)) + { + int pos = _cursorPosition + 1; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousChar)) + { + int pos = _cursorPosition - 1; + setSelection(pos); + setCursorPos(pos); + } + if (event->matches(QKeySequence::SelectEndOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousLine)) + { + int pos = _cursorPosition - (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectNextLine)) + { + int pos = _cursorPosition + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + + if (event->matches(QKeySequence::SelectNextPage)) + { + int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousPage)) + { + int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectEndOfDocument)) + { + int pos = _xData.size() * 2; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfDocument)) + { + int pos = 0; + setCursorPos(pos); + setSelection(pos); + } + +/*****************************************************************************/ +/* Edit Commands */ +/*****************************************************************************/ +if (!_readOnly) +{ + /* Hex input */ + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + + // If insert mode, then insert a byte + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + } + + // Change content + if (_xData.size() > 0) + { + QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + + replace(posBa, QByteArray().fromHex(hexValue)[0]); + + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + } + + /* Cut & Paste */ + if (event->matches(QKeySequence::Cut)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append("\n"); + } + remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + setCursorPos(getSelectionBegin()); + resetSelection(getSelectionBegin()); + } + + if (event->matches(QKeySequence::Paste)) + { + QClipboard *clipboard = QApplication::clipboard(); + QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); + insert(_cursorPosition / 2, ba); + setCursorPos(_cursorPosition + 2 * ba.length()); + resetSelection(getSelectionBegin()); + } + + + /* Delete char */ + if (event->matches(QKeySequence::Delete)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (_overwriteMode) + replace(posBa, char(0)); + else + remove(posBa, 1); + } + } + + /* Backspace */ + if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (posBa > 0) + { + if (_overwriteMode) + replace(posBa - 1, char(0)); + else + remove(posBa - 1, 1); + setCursorPos(_cursorPosition - 2); + } + } + } + + /* undo */ + if (event->matches(QKeySequence::Undo)) + { + undo(); + } + + /* redo */ + if (event->matches(QKeySequence::Redo)) + { + redo(); + } + + } + + if (event->matches(QKeySequence::Copy)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append('\n'); + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + } + + // Switch between insert/overwrite mode + if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) + { + _overwriteMode = !_overwriteMode; + setCursorPos(_cursorPosition); + overwriteModeChanged(_overwriteMode); + } + + _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); update(); } +void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int actPos = cursorPos(event->pos()); + setCursorPos(actPos); + setSelection(actPos); +} + void QHexEditPrivate::mousePressEvent(QMouseEvent * event) { - setCursorPos(event->pos()); + _blink = false; + update(); + int cPos = cursorPos(event->pos()); + resetSelection(cPos); + setCursorPos(cPos); } void QHexEditPrivate::paintEvent(QPaintEvent *event) @@ -256,8 +576,8 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event) if (firstLineIdx < 0) firstLineIdx = 0; int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; - if (lastLineIdx > _data.size()) - lastLineIdx = _data.size(); + if (lastLineIdx > _xData.size()) + lastLineIdx = _xData.size(); int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; // paint address area @@ -266,33 +586,51 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event) for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { QString address = QString("%1") - .arg(lineIdx + _addressOffset, _realAddressNumbers, 16, QChar('0')); + .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); painter.drawText(_xPosAdr, yPos, address); } } // paint hex area - QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); QBrush highLighted = QBrush(_highlightingColor); - painter.setBackground(highLighted); + QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); + QBrush selected = QBrush(_selectionColor); + QPen colSelected = QPen(Qt::white); + QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); + painter.setBackgroundMode(Qt::TransparentMode); + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { QByteArray hex; int xPos = _xPosHex; - for (int colIdx = 0; ((lineIdx + colIdx) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) { - // hilight diff bytes - if (_highlighting) + int posBa = lineIdx + colIdx; + if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) { - int posBa = lineIdx + colIdx; - if (posBa >= _originalData.size()) - painter.setBackgroundMode(Qt::TransparentMode); - else - if (_data[posBa] == _originalData[posBa]) - painter.setBackgroundMode(Qt::TransparentMode); - else + painter.setBackground(selected); + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setPen(colSelected); + } + else + { + if (_highlighting) + { + // hilight diff bytes + painter.setBackground(highLighted); + if (_xData.dataChanged(posBa)) + { + painter.setPen(colHighlighted); painter.setBackgroundMode(Qt::OpaqueMode); + } + else + { + painter.setPen(colStandard); + painter.setBackgroundMode(Qt::TransparentMode); + } + } } // render hex value @@ -306,26 +644,40 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event) painter.drawText(xPos, yPos, hex); xPos += 3 * _charWidth; } + } } painter.setBackgroundMode(Qt::TransparentMode); + painter.setPen(this->palette().color(QPalette::WindowText)); // paint ascii area if (_asciiArea) { for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { - QByteArray ascii = _data.mid(lineIdx, BYTES_PER_LINE); - for (int idx=0; idx < ascii.size(); idx++) - if (((char)ascii[idx] < 0x20) or ((char)ascii[idx] > 0x7e)) - ascii[idx] = '.'; - painter.drawText(_xPosAscii, yPos, ascii); + int xPosAscii = _xPosAscii; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); + xPosAscii += _charWidth; + } } } // paint cursor - if ((_data.size() > 0) and _blink) - painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText)); + if (_blink) + { + if (_overwriteMode) + painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); + else + painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); + } + + if (_size != _xData.size()) + { + _size = _xData.size(); + emit currentSizeChanged(_size); + } } void QHexEditPrivate::setCursorPos(int position) @@ -337,11 +689,11 @@ void QHexEditPrivate::setCursorPos(int position) // cursor in range? if (_overwriteMode) { - if (position > (_data.size() * 2 - 1)) - position = _data.size() * 2 - 1; + if (position > (_xData.size() * 2 - 1)) + position = _xData.size() * 2 - 1; } else { - if (position > (_data.size() * 2)) - position = _data.size() * 2; + if (position > (_xData.size() * 2)) + position = _xData.size() * 2; } if (position < 0) @@ -356,11 +708,12 @@ void QHexEditPrivate::setCursorPos(int position) // immiadately draw cursor _blink = true; update(); - emit currentAddress(_cursorPosition/2); + emit currentAddressChanged(_cursorPosition/2); } -void QHexEditPrivate::setCursorPos(QPoint pos) +int QHexEditPrivate::cursorPos(QPoint pos) { + int result = -1; // find char under cursor if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) { @@ -369,11 +722,55 @@ void QHexEditPrivate::setCursorPos(QPoint pos) x = (x / 3) * 2; else x = ((x / 3) * 2) + 1; - int y = (pos.y() / _charHeight) * 2 * BYTES_PER_LINE; - setCursorPos(x + y); + int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; + result = x + y; + } + return result; +} + +int QHexEditPrivate::cursorPos() +{ + return _cursorPosition; +} + +void QHexEditPrivate::resetSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + _selectionInit = pos; + _selectionBegin = pos; + _selectionEnd = pos; +} + +void QHexEditPrivate::setSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + if (pos >= _selectionInit) + { + _selectionEnd = pos; + _selectionBegin = _selectionInit; + } + else + { + _selectionBegin = pos; + _selectionEnd = _selectionInit; } } +int QHexEditPrivate::getSelectionBegin() +{ + return _selectionBegin; +} + +int QHexEditPrivate::getSelectionEnd() +{ + return _selectionEnd; +} + + void QHexEditPrivate::updateCursor() { if (_blink) @@ -388,26 +785,15 @@ void QHexEditPrivate::adjust() _charWidth = fontMetrics().width(QLatin1Char('9')); _charHeight = fontMetrics().height(); - // is addressNumbers wide enought? - QString test = QString("%1") - .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); - _realAddressNumbers = test.size(); - _xPosAdr = 0; if (_addressArea) - _xPosHex = _realAddressNumbers *_charWidth + GAP_ADR_HEX; + _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; else _xPosHex = 0; _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; - if (_overwriteMode) - _cursorWidth = _charWidth; - else - _cursorWidth = 2; - _cursorHeight = _charHeight - 3; - // tell QAbstractScollbar, how big we are - setMinimumHeight(((_data.size()/16 + 1) * _charHeight) + 3); + setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); update(); diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h index c422c58..b802af3 100644 --- a/extra/qhexedit2/src/qhexedit_p.h +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -5,6 +5,7 @@ #include +#include "xbytearray.h" class QHexEditPrivate : public QWidget { @@ -13,24 +14,37 @@ Q_OBJECT public: QHexEditPrivate(QScrollArea *parent); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setAddressOffset(int offset); int addressOffset(); + void setCursorPos(int position); + int cursorPos(); + void setData(QByteArray const &data); QByteArray data(); - void setAddressAreaColor(QColor const &color); - QColor addressAreaColor(); - void setHighlightingColor(QColor const &color); QColor highlightingColor(); void setOverwriteMode(bool overwriteMode); bool overwriteMode(); - void insert(int i, const QByteArray & ba); - void insert(int i, char ch); + void setReadOnly(bool readOnly); + bool isReadOnly(); + + void setSelectionColor(QColor const &color); + QColor selectionColor(); + + XByteArray & xData(); + + void insert(int index, const QByteArray & ba); + void insert(int index, char ch); void remove(int index, int len=1); + void replace(int index, char ch); + void replace(int index, const QByteArray & ba); void setAddressArea(bool addressArea); void setAddressWidth(int addressWidth); @@ -38,17 +52,32 @@ public: void setHighlighting(bool mode); virtual void setFont(const QFont &font); + void undo(); + void redo(); + + QString toRedableString(); + QString selectionToReadableString(); + signals: - void currentAddress(int address); + void currentAddressChanged(int address); + void currentSizeChanged(int size); void dataChanged(); void overwriteModeChanged(bool state); protected: void keyPressEvent(QKeyEvent * event); + void mouseMoveEvent(QMouseEvent * event); void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *event); - void setCursorPos(QPoint pos); - void setCursorPos(int position); + + int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION + + void resetSelection(int pos); + void setSelection(int pos); // set min (if below init) or max (if greater init) + int getSelectionBegin(); + int getSelectionEnd(); + private slots: void updateCursor(); @@ -57,23 +86,32 @@ private: void adjust(); QColor _addressAreaColor; - QByteArray _data; - QByteArray _originalData; QColor _highlightingColor; + QColor _selectionColor; QScrollArea *_scrollArea; QTimer _cursorTimer; + QUndoStack *_undoStack; - bool _blink; - bool _addressArea; - bool _asciiArea; - bool _highlighting; + XByteArray _xData; // Hält den Inhalt des Hex Editors + + bool _blink; // true: then cursor blinks + bool _renderingRequired; // Flag to store that rendering is necessary + bool _addressArea; // left area of QHexEdit + bool _asciiArea; // medium area + bool _highlighting; // highlighting of changed bytes bool _overwriteMode; + bool _readOnly; // true: the user can only look and navigate - int _addressNumbers, _realAddressNumbers; - int _addressOffset; - int _charWidth, _charHeight; - int _cursorX, _cursorY, _cursorWidth, _cursorHeight, _cursorPosition; - int _xPosAdr, _xPosHex, _xPosAscii; + int _charWidth, _charHeight; // char dimensions (dpendend on font) + int _cursorX, _cursorY; // graphics position of the cursor + int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) + int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas + + int _selectionBegin; // First selected char + int _selectionEnd; // Last selected char + int _selectionInit; // That's, where we pressed the mouse button + + int _size; }; /** \endcond docNever */ diff --git a/extra/qhexedit2/src/xbytearray.cpp b/extra/qhexedit2/src/xbytearray.cpp new file mode 100644 index 0000000..ec8bf3d --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.cpp @@ -0,0 +1,167 @@ +#include "xbytearray.h" + +XByteArray::XByteArray() +{ + _oldSize = -99; + _addressNumbers = 4; + _addressOffset = 0; + +} + +int XByteArray::addressOffset() +{ + return _addressOffset; +} + +void XByteArray::setAddressOffset(int offset) +{ + _addressOffset = offset; +} + +int XByteArray::addressWidth() +{ + return _addressNumbers; +} + +void XByteArray::setAddressWidth(int width) +{ + if ((width >= 0) and (width<=6)) + { + _addressNumbers = width; + } +} + +QByteArray & XByteArray::data() +{ + return _data; +} + +void XByteArray::setData(QByteArray data) +{ + _data = data; + _changedData = QByteArray(data.length(), char(0)); +} + +bool XByteArray::dataChanged(int i) +{ + return bool(_changedData[i]); +} + +QByteArray XByteArray::dataChanged(int i, int len) +{ + return _changedData.mid(i, len); +} + +void XByteArray::setDataChanged(int i, bool state) +{ + _changedData[i] = char(state); +} + +void XByteArray::setDataChanged(int i, const QByteArray & state) +{ + int length = state.length(); + int len; + if ((i + length) > _changedData.length()) + len = _changedData.length() - i; + else + len = length; + _changedData.replace(i, len, state); +} + +int XByteArray::realAddressNumbers() +{ + if (_oldSize != _data.size()) + { + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + } + return _realAddressNumbers; +} + +int XByteArray::size() +{ + return _data.size(); +} + +QByteArray & XByteArray::insert(int i, char ch) +{ + _data.insert(i, ch); + _changedData.insert(i, char(1)); + return _data; +} + +QByteArray & XByteArray::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _changedData.insert(i, QByteArray(ba.length(), char(1))); + return _data; +} + +QByteArray & XByteArray::remove(int i, int len) +{ + _data.remove(i, len); + _changedData.remove(i, len); + return _data; +} + +QByteArray & XByteArray::replace(int index, char ch) +{ + _data[index] = ch; + _changedData[index] = char(1); + return _data; +} + +QByteArray & XByteArray::replace(int index, const QByteArray & ba) +{ + int len = ba.length(); + return replace(index, len, ba); +} + +QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) +{ + int len; + if ((index + length) > _data.length()) + len = _data.length() - index; + else + len = length; + _data.replace(index, len, ba.mid(0, len)); + _changedData.replace(index, len, QByteArray(len, char(1))); + return _data; +} + +QChar XByteArray::asciiChar(int index) +{ + char ch = _data[index]; + if ((ch < 0x20) or (ch > 0x7e)) + ch = '.'; + return QChar(ch); +} + +QString XByteArray::toRedableString(int start, int end) +{ + int adrWidth = realAddressNumbers(); + if (_addressNumbers > adrWidth) + adrWidth = _addressNumbers; + if (end < 0) + end = _data.size(); + + QString result; + for (int i=start; i < end; i += 16) + { + QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); + QString hexStr; + QString ascStr; + for (int j=0; j<16; j++) + { + if ((i + j) < _data.size()) + { + hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); + ascStr.append(asciiChar(i+j)); + } + } + result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; + } + return result; +} diff --git a/extra/qhexedit2/src/xbytearray.h b/extra/qhexedit2/src/xbytearray.h new file mode 100644 index 0000000..2b67c61 --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.h @@ -0,0 +1,66 @@ +#ifndef XBYTEARRAY_H +#define XBYTEARRAY_H + +/** \cond docNever */ + +#include + +/*! XByteArray represents the content of QHexEcit. +XByteArray comprehend the data itself and informations to store if it was +changed. The QHexEdit component uses these informations to perform nice +rendering of the data + +XByteArray also provides some functionality to insert, replace and remove +single chars and QByteArras. Additionally some functions support rendering +and converting to readable strings. +*/ +class XByteArray +{ +public: + explicit XByteArray(); + + int addressOffset(); + void setAddressOffset(int offset); + + int addressWidth(); + void setAddressWidth(int width); + + QByteArray & data(); + void setData(QByteArray data); + + bool dataChanged(int i); + QByteArray dataChanged(int i, int len); + void setDataChanged(int i, bool state); + void setDataChanged(int i, const QByteArray & state); + + int realAddressNumbers(); + int size(); + + QByteArray & insert(int i, char ch); + QByteArray & insert(int i, const QByteArray & ba); + + QByteArray & remove(int pos, int len); + + QByteArray & replace(int index, char ch); + QByteArray & replace(int index, const QByteArray & ba); + QByteArray & replace(int index, int length, const QByteArray & ba); + + QChar asciiChar(int index); + QString toRedableString(int start=0, int end=-1); + +signals: + +public slots: + +private: + QByteArray _data; + QByteArray _changedData; + + int _addressNumbers; // wanted width of address area + int _addressOffset; // will be added to the real addres inside bytearray + int _realAddressNumbers; // real width of address area (can be greater then wanted width) + int _oldSize; // size of data +}; + +/** \endcond docNever */ +#endif // XBYTEARRAY_H From c9a7523ef927e6ed254753c9e1201a510d072765 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 23 Oct 2011 22:35:43 +0530 Subject: [PATCH 189/294] Fixed crash on drone exit (on Windows) - now we do cleanup --- server/pcapport.cpp | 28 +++++++++++++++++++++++++--- server/pcapport.h | 2 ++ server/winpcapport.cpp | 13 +++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index cefffb3..9bd2842 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -127,10 +127,22 @@ void PcapPort::init() PcapPort::~PcapPort() { qDebug("In %s", __FUNCTION__); + + if (monitorRx_) + monitorRx_->stop(); + if (monitorTx_) + monitorTx_->stop(); + delete capturer_; delete transmitter_; - delete monitorTx_; + + if (monitorRx_) + monitorRx_->wait(); delete monitorRx_; + + if (monitorTx_) + monitorTx_->wait(); + delete monitorTx_; } void PcapPort::updateNotes() @@ -167,6 +179,7 @@ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, isPromisc_ = true; noLocalCapture = true; stats_ = stats; + stop_ = false; _retry: #ifdef Q_OS_WIN32 @@ -244,7 +257,7 @@ PcapPort::PortMonitor::~PortMonitor() void PcapPort::PortMonitor::run() { - while (1) + while (!stop_) { int ret; struct pcap_pkthdr *hdr; @@ -283,12 +296,21 @@ void PcapPort::PortMonitor::run() __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } } } +void PcapPort::PortMonitor::stop() +{ + stop_ = true; + pcap_breakloop(handle()); +} + PcapPort::PortTransmitter::PortTransmitter(const char *device) { char errbuf[PCAP_ERRBUF_SIZE] = ""; @@ -434,7 +456,7 @@ void PcapPort::PortTransmitter::setHandle(pcap_t *handle) if (usingInternalHandle_) pcap_close(handle_); handle_ = handle; - usingInternalStats_ = false; + usingInternalHandle_ = false; } void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) diff --git a/server/pcapport.h b/server/pcapport.h index 814528d..d05018b 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -82,12 +82,14 @@ protected: AbstractPort::PortStats *stats); ~PortMonitor(); void run(); + void stop(); pcap_t* handle() { return handle_; } Direction direction() { return direction_; } bool isDirectional() { return isDirectional_; } bool isPromiscuous() { return isPromisc_; } protected: AbstractPort::PortStats *stats_; + bool stop_; private: pcap_t *handle_; Direction direction_; diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index 4911581..bbd49d8 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -29,6 +29,11 @@ const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; WinPcapPort::WinPcapPort(int id, const char *device) : PcapPort(id, device) { + monitorRx_->stop(); + monitorTx_->stop(); + monitorRx_->wait(); + monitorTx_->wait(); + delete monitorRx_; delete monitorTx_; @@ -140,7 +145,7 @@ void WinPcapPort::PortMonitor::run() lastTs.tv_sec = 0; lastTs.tv_usec = 0; - while (1) + while (!stop_) { int ret; struct pcap_pkthdr *hdr; @@ -205,12 +210,16 @@ void WinPcapPort::PortMonitor::run() __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); break; case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } lastTs.tv_sec = hdr->ts.tv_sec; lastTs.tv_usec = hdr->ts.tv_usec; - QThread::msleep(1000); + if (!stop_) + QThread::msleep(1000); } } From 49e3acf32777c8b20ca45e6971ec75d23dbeca5d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 24 Oct 2011 07:22:22 +0530 Subject: [PATCH 190/294] Bumped version to 0.5 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index ed0d8da..8eb45f0 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.4.1 +APP_VERSION = 0.5 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From eedc6f62ff1212a5b0cd1ceb6a1bff05ccd059ee Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 29 Oct 2011 07:40:18 +0530 Subject: [PATCH 191/294] Fixed incorrect very long delay in "interleaved streams" mode --- server/abstractport.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 55e31a3..178338a 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -528,10 +528,15 @@ void AbstractPort::updatePacketListInterleaved() } } while ((sec < durSec) || (nsec < durNsec)); - quint64 delaySec = durSec - lastPktTxSec; - quint64 delayNsec = durNsec - lastPktTxNsec; - qDebug("loop Delay = %" PRIu64 "/%" PRIu64, delaySec, delayNsec); - setPacketListLoopMode(true, durSec - lastPktTxSec, durNsec - lastPktTxNsec); + qint64 delaySec = durSec - lastPktTxSec; + qint64 delayNsec = durNsec - lastPktTxNsec; + while (delayNsec < 0) + { + delayNsec += long(1e9); + delaySec--; + } + qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); + setPacketListLoopMode(true, delaySec, delayNsec); isSendQueueDirty_ = false; } From 8ef427b0fd4a2eccbf034dcc8164f802d3ec6fa9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 22 Dec 2011 19:29:19 +0530 Subject: [PATCH 192/294] Fixed incorrect TCP checksum when it is overrided Fixes issue 58 --- common/tcp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/tcp.cpp b/common/tcp.cpp index c08ca74..39c71bf 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -670,6 +670,7 @@ void TcpProtocol::loadConfigWidget() void TcpProtocol::storeConfigWidget() { + bool isOk; int ff = 0; configWidget(); @@ -690,7 +691,8 @@ void TcpProtocol::storeConfigWidget() setFieldData(tcp_window, configForm->leTcpWindow->text()); - setFieldData(tcp_cksum, configForm->leTcpCksum->text()); + setFieldData(tcp_cksum, configForm->leTcpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); setFieldData(tcp_is_override_cksum, configForm->cbTcpCksumOverride->isChecked()); From 88cb2945674d9c7db3109c2c640c3c12a68a9695 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 8 Feb 2012 22:14:20 +0530 Subject: [PATCH 193/294] Added support for rx error stats on Linux. Updates issue 28 --- client/portstatsmodel.cpp | 5 +++++ client/portstatsmodel.h | 12 +++++++++++- common/protocol.proto | 5 +++++ server/abstractport.cpp | 5 +++++ server/abstractport.h | 5 +++++ server/linuxport.cpp | 16 ++++++++++++---- server/myservice.cpp | 5 +++++ 7 files changed, 48 insertions(+), 5 deletions(-) diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index 92e6a24..ff9d7b7 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -166,6 +166,11 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const case e_STAT_BYTES_SENT_NIC: return stats.tx_bytes_nic(); #endif + case e_STAT_RX_DROPS : return quint64(stats.rx_drops()); + case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); + case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); + case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors()); + default: qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, index.row()); diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h index d50508b..a0d6868 100644 --- a/client/portstatsmodel.h +++ b/client/portstatsmodel.h @@ -53,7 +53,13 @@ typedef enum { e_STAT_BYTES_SENT_NIC, #endif - e_STATISTICS_END = e_STAT_BYTE_RECV_RATE, + // Rx Errors + e_STAT_RX_DROPS, + e_STAT_RX_ERRORS, + e_STAT_RX_FIFO_ERRORS, + e_STAT_RX_FRAME_ERRORS, + + e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, e_STAT_MAX } PortStat; @@ -77,6 +83,10 @@ static QStringList PortStatName = (QStringList() << "Bytes Received (NIC)" << "Bytes Sent (NIC)" #endif + << "Receive Drops" + << "Receive Errors" + << "Receive Fifo Errors" + << "Receive Frame Errors" ); static QStringList LinkStateName = (QStringList() diff --git a/common/protocol.proto b/common/protocol.proto index c902610..9a74654 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -214,6 +214,11 @@ message PortStats { optional uint64 tx_bytes_nic = 24; optional uint64 tx_pps = 25; optional uint64 tx_bps = 26; + + optional uint64 rx_drops = 100; + optional uint64 rx_errors = 101; + optional uint64 rx_fifo_errors = 102; + optional uint64 rx_frame_errors = 103; } message PortStatsList { diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 178338a..c210e56 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -551,4 +551,9 @@ void AbstractPort::stats(PortStats *stats) stats->txBytes = stats_.txBytes - epochStats_.txBytes; stats->txPps = stats_.txPps; stats->txBps = stats_.txBps; + + stats->rxDrops = stats_.rxDrops - epochStats_.rxDrops; + stats->rxErrors = stats_.rxErrors - epochStats_.rxErrors; + stats->rxFifoErrors = stats_.rxFifoErrors - epochStats_.rxFifoErrors; + stats->rxFrameErrors = stats_.rxFrameErrors - epochStats_.rxFrameErrors; } diff --git a/server/abstractport.h b/server/abstractport.h index f531e26..288f898 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -38,6 +38,11 @@ public: quint64 rxPps; quint64 rxBps; + quint64 rxDrops; + quint64 rxErrors; + quint64 rxFifoErrors; + quint64 rxFrameErrors; + quint64 txPkts; quint64 txBytes; quint64 txPps; diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 12f5cb3..0f041f1 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -132,8 +132,8 @@ void LinuxPort::StatsMonitor::run() char *p, *end; int count, index; const char* fmtopt[] = { - "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u\n", - "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u%n\n", + "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", }; const char *fmt; struct ifreq ifr; @@ -299,6 +299,7 @@ void LinuxPort::StatsMonitor::run() { uint dummy; quint64 rxBytes, rxPkts; + quint64 rxErrors, rxDrops, rxFifo, rxFrame; quint64 txBytes, txPkts; // Skip interface name - we assume the number and order of ports @@ -315,8 +316,10 @@ void LinuxPort::StatsMonitor::run() p++; sscanf(p, fmt, - &rxBytes, &rxPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, - &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); + &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, + &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy); if (index < count) { @@ -331,6 +334,11 @@ void LinuxPort::StatsMonitor::run() stats->txBps = (txBytes - stats->txBytes)/kRefreshFreq_; stats->txPkts = txPkts; stats->txBytes = txBytes; + + stats->rxDrops = rxDrops; + stats->rxErrors = rxErrors; + stats->rxFifoErrors = rxFifo; + stats->rxFrameErrors = rxFrame; } } diff --git a/server/myservice.cpp b/server/myservice.cpp index 424a95a..be0984a 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -441,6 +441,11 @@ void MyService::getStats(::google::protobuf::RpcController* /*controller*/, s->set_tx_bytes(stats.txBytes); s->set_tx_pps(stats.txPps); s->set_tx_bps(stats.txBps); + + s->set_rx_drops(stats.rxDrops); + s->set_rx_errors(stats.rxErrors); + s->set_rx_fifo_errors(stats.rxFifoErrors); + s->set_rx_frame_errors(stats.rxFrameErrors); } done->Run(); From 5f99e9456c505b7c1e837062a20bb5de4f3edef6 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 12 Feb 2012 12:55:26 +0530 Subject: [PATCH 194/294] BSD specific interface stats and error stats, including link state Update issue 28 --- server/abstractport.cpp | 20 ++- server/abstractport.h | 4 +- server/bsdport.cpp | 327 ++++++++++++++++++++++++++++++++++++++++ server/bsdport.h | 61 ++++++++ server/drone.pro | 1 + server/portmanager.cpp | 5 +- 6 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 server/bsdport.cpp create mode 100644 server/bsdport.h diff --git a/server/abstractport.cpp b/server/abstractport.cpp index c210e56..d121bcf 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" @@ -121,6 +121,24 @@ bool AbstractPort::deleteStream(int streamId) return false; } +void AbstractPort::addNote(QString note) +{ + QString notes = QString::fromStdString(data_.notes()); + + note.prepend("
  • "); + note.append("
  • "); + + if (notes.isEmpty()) + notes="Limitation(s)
      "; + else + notes.remove("
    "); + + notes.append(note); + notes.append(""); + + data_.set_notes(notes.toStdString()); +} + void AbstractPort::updatePacketList() { switch(data_.transmit_mode()) diff --git a/server/abstractport.h b/server/abstractport.h index 288f898..b9c7a6e 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" @@ -97,6 +97,8 @@ public: void resetStats() { epochStats_ = stats_; } protected: + void addNote(QString note); + void updatePacketListSequential(); void updatePacketListInterleaved(); diff --git a/server/bsdport.cpp b/server/bsdport.cpp new file mode 100644 index 0000000..016e93e --- /dev/null +++ b/server/bsdport.cpp @@ -0,0 +1,327 @@ +/* +Copyright (C) 2012 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 "bsdport.h" + +#ifdef Q_OS_BSD4 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QList BsdPort::allPorts_; +BsdPort::StatsMonitor *BsdPort::monitor_; + +BsdPort::BsdPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Bsd + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); +} + +BsdPort::~BsdPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if (ifr.ifr_flagshigh & promisc) + { + ifr.ifr_flagshigh &= ~promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + else + qDebug("Cleared promisc successfully"); + } + else + qDebug("clear_promisc is set but IFF_PPROMISC is not?"); + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void BsdPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +bool BsdPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool BsdPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +BsdPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; +} + +void BsdPort::StatsMonitor::run() +{ + int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + const int mibLen = sizeof(mib)/sizeof(mib[0]); + QHash portStats; + QHash linkState; + int sd; + QByteArray buf; + size_t len; + char *p, *end; + int count; + struct ifreq ifr; + + // + // We first setup stuff before we start polling for stats + // + if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); + return; + } + + qDebug("sysctl mib returns reqd len = %d\n", len); + len *= 2; // for extra room, just in case! + buf.fill('\0', len); + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); + return; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + + // + // Populate the port stats hash table + // + p = buf.data(); + end = p + len; + count = 0; + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); + + if (ifm->ifm_type == RTM_IFINFO) + { + char ifname[1024]; + + strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + + qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); + foreach(BsdPort* port, allPorts_) + { + if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) + { + Q_ASSERT(ifm->ifm_index == sdl->sdl_index); + portStats[uint(ifm->ifm_index)] = &(port->stats_); + linkState[uint(ifm->ifm_index)] = &(port->linkState_); + + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if ((ifr.ifr_flagshigh & promisc) == 0) + { + ifr.ifr_flagshigh |= promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + { + qDebug("%s: set promisc successful", + port->name()); + port->clearPromisc_ = true; + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + else + qDebug("%s: promisc already set", port->name()); + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + count++; + } + p += ifm->ifm_msglen; + } + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in NET_RT_IFLIST - no stats will be available"); + return; + } + + close(sd); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); + goto _try_later; + } + + p = buf.data(); + end = p + len; + + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + AbstractPort::PortStats *stats; + + if (ifm->ifm_type != RTM_IFINFO) + goto _next; + + stats = portStats[ifm->ifm_index]; + if (stats) + { + struct if_data *ifd = &(ifm->ifm_data); + OstProto::LinkState *state = linkState[ifm->ifm_index]; + + Q_ASSERT(state); + *state = (OstProto::LinkState) ifd->ifi_link_state; + + stats->rxPps = (ifd->ifi_ipackets + ifd->ifi_noproto + - stats->rxPkts) /kRefreshFreq_; + stats->rxBps = (ifd->ifi_ibytes - stats->rxBytes) + /kRefreshFreq_; + stats->rxPkts = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxBytes = ifd->ifi_ibytes; + stats->txPps = (ifd->ifi_opackets - stats->txPkts) + /kRefreshFreq_; + stats->txBps = (ifd->ifi_obytes - stats->txBytes) + /kRefreshFreq_; + stats->txPkts = ifd->ifi_opackets; + stats->txBytes = ifd->ifi_obytes; + + stats->rxDrops = ifd->ifi_iqdrops; + stats->rxErrors = ifd->ifi_ierrors; + } +_next: + p += ifm->ifm_msglen; + } +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); +} + +void BsdPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/bsdport.h b/server/bsdport.h new file mode 100644 index 0000000..776c39a --- /dev/null +++ b/server/bsdport.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2012 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 +*/ + +#ifndef _SERVER_BSD_PORT_H +#define _SERVER_BSD_PORT_H + +#include + +#ifdef Q_OS_BSD4 + +#include "pcapport.h" + +class BsdPort : public PcapPort +{ +public: + BsdPort(int id, const char *device); + ~BsdPort(); + + void init(); + + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/drone.pro b/server/drone.pro index e235c24..341634c 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -36,6 +36,7 @@ SOURCES += \ portmanager.cpp \ abstractport.cpp \ pcapport.cpp \ + bsdport.cpp \ linuxport.cpp \ winpcapport.cpp SOURCES += myservice.cpp diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 9a47ae7..2981464 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" @@ -22,6 +22,7 @@ along with this program. If not, see #include #include +#include "bsdport.h" #include "linuxport.h" #include "pcapport.h" #include "winpcapport.h" @@ -52,6 +53,8 @@ PortManager::PortManager() port = new WinPcapPort(i, device->name); #elif defined(Q_OS_LINUX) port = new LinuxPort(i, device->name); +#elif defined(Q_OS_BSD4) + port = new BsdPort(i, device->name); #else port = new PcapPort(i, device->name); #endif From c51e28b2036c4ffef327cef32b4fa4819d348cc5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 15 Feb 2012 07:43:51 +0530 Subject: [PATCH 195/294] Updated copyright years in the About dialog --- client/about.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/about.ui b/client/about.ui index 56cf1a5..4727324 100644 --- a/client/about.ui +++ b/client/about.ui @@ -94,7 +94,7 @@ - Copyright (c) 2007-2011 Srivats P. + Copyright (c) 2007-2012 Srivats P. Qt::AlignCenter From 0d253cdb098836097a468eb2d7683086515400b0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 16 Feb 2012 21:48:27 +0530 Subject: [PATCH 196/294] Interface stats and link state for Mac OSX --- server/bsdport.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/bsdport.cpp b/server/bsdport.cpp index 016e93e..2382bcb 100644 --- a/server/bsdport.cpp +++ b/server/bsdport.cpp @@ -35,6 +35,11 @@ along with this program. If not, see #include #include +#ifdef Q_OS_MAC +#define ifr_flagshigh ifr_flags +#define IFF_PPROMISC (IFF_PROMISC << 16) +#endif + QList BsdPort::allPorts_; BsdPort::StatsMonitor *BsdPort::monitor_; @@ -154,7 +159,7 @@ void BsdPort::StatsMonitor::run() return; } - qDebug("sysctl mib returns reqd len = %d\n", len); + qDebug("sysctl mib returns reqd len = %d\n", (int) len); len *= 2; // for extra room, just in case! buf.fill('\0', len); if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) @@ -275,7 +280,12 @@ void BsdPort::StatsMonitor::run() OstProto::LinkState *state = linkState[ifm->ifm_index]; Q_ASSERT(state); +#ifdef Q_OS_MAC + *state = ifm->ifm_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; +#else *state = (OstProto::LinkState) ifd->ifi_link_state; +#endif stats->rxPps = (ifd->ifi_ipackets + ifd->ifi_noproto - stats->rxPkts) /kRefreshFreq_; From 87ee8244fcc20897b414679efbd9e60e8173f688 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 7 Mar 2012 19:25:09 +0530 Subject: [PATCH 197/294] Fix for segfault when a port failed to open due to any reason Fixes issue 64 --- server/pcapport.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 9bd2842..7dc6d48 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -252,7 +252,8 @@ _open_error: PcapPort::PortMonitor::~PortMonitor() { - pcap_close(handle_); + if (handle_) + pcap_close(handle_); } void PcapPort::PortMonitor::run() From 31c7f08e7cc46114d3fe740a07409d4c8bf5f094 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 9 Mar 2012 22:15:06 +0530 Subject: [PATCH 198/294] Fixed Win32 QPC timestamp diff that caused extraordinarily large inter-packet timing --- server/pcapport.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 7dc6d48..5244956 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -59,7 +59,7 @@ static void inline getTimeStamp(TimeStamp* stamp) static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) { - if (end->QuadPart > start->QuadPart) + if (end->QuadPart >= start->QuadPart) return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; else { @@ -531,6 +531,7 @@ _restart: getTimeStamp(&ovrEnd); overHead += seq->usecDuration_ - udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); } if (stop_) ret = -2; @@ -614,6 +615,7 @@ int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, getTimeStamp(&ovrEnd); overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); usec += overHead; if (usec > 0) { From 9cac3cd91857be34e85687906fe7f40157c10d51 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 10 Mar 2012 13:29:41 +0530 Subject: [PATCH 199/294] Linux now uses stats using netlink - if that isn't available for some reason, we fallback to the /proc stats. If netlink stats is used, link state info is available. --- server/linuxport.cpp | 436 +++++++++++++++++++++++++++++++++++++++---- server/linuxport.h | 9 + 2 files changed, 413 insertions(+), 32 deletions(-) diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 0f041f1..74e7c46 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -22,6 +22,8 @@ along with this program. If not, see #ifdef Q_OS_LINUX #include +#include +#include #include #include @@ -31,12 +33,15 @@ along with this program. If not, see #include #include +#include + QList LinuxPort::allPorts_; LinuxPort::StatsMonitor *LinuxPort::monitor_; LinuxPort::LinuxPort(int id, const char *device) : PcapPort(id, device) { + isPromisc_ = true; clearPromisc_ = false; // We don't need per port Rx/Tx monitors for Linux @@ -93,15 +98,17 @@ LinuxPort::~LinuxPort() void LinuxPort::init() { - // TODO: Update Notes with Promisc/Non-Promisc - if (!monitor_->isRunning()) monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); } OstProto::LinkState LinuxPort::linkState() { - // TODO return linkState_; } @@ -121,12 +128,29 @@ LinuxPort::StatsMonitor::StatsMonitor() : QThread() { stop_ = false; + setupDone_ = false; + ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(ioctlSocket_ >= 0); +} + +LinuxPort::StatsMonitor::~StatsMonitor() +{ + close(ioctlSocket_); } void LinuxPort::StatsMonitor::run() +{ + if (netlinkStats() < 0) + { + qDebug("netlink stats not available - using /proc stats"); + procStats(); + } +} + +void LinuxPort::StatsMonitor::procStats() { PortStats **portStats; - int fd, sd; + int fd; QByteArray buf; int len; char *p, *end; @@ -136,7 +160,6 @@ void LinuxPort::StatsMonitor::run() "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", }; const char *fmt; - struct ifreq ifr; // // We first setup stuff before we start polling for stats @@ -184,10 +207,6 @@ void LinuxPort::StatsMonitor::run() portStats = (PortStats**) calloc(count, sizeof(PortStats)); Q_ASSERT(portStats != NULL); - sd = socket(AF_INET, SOCK_DGRAM, 0); - Q_ASSERT(sd >= 0); - memset(&ifr, 0, sizeof(ifr)); - // // Populate the port stats array // @@ -224,28 +243,11 @@ void LinuxPort::StatsMonitor::run() { portStats[index] = &(port->stats_); - // Set promisc mode, if not already set - strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); - if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) - { - if ((ifr.ifr_flags & IFF_PROMISC) == 0) - { - ifr.ifr_flags |= IFF_PROMISC; - if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) - port->clearPromisc_ = true; - else - { - qDebug("%s: failed to set promisc; " - "SIOCSIFFLAGS failed (%s)", - port->name(), strerror(errno)); - } - } - } + if (setPromisc(port->name())) + port->clearPromisc_ = true; else - { - qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", - port->name(), strerror(errno)); - } + port->isPromisc_ = false; + break; } } @@ -260,9 +262,8 @@ void LinuxPort::StatsMonitor::run() } Q_ASSERT(index == count); - close(sd); - qDebug("stats for %d ports setup", count); + setupDone_ = true; // // We are all set - Let's start polling for stats! @@ -353,8 +354,379 @@ void LinuxPort::StatsMonitor::run() free(portStats); } +int LinuxPort::StatsMonitor::netlinkStats() +{ + QHash portStats; + QHash linkState; + int fd; + struct sockaddr_nl local; + struct sockaddr_nl kernel; + QByteArray buf; + int len, count; + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtg; + } ifListReq; + struct iovec iov; + struct msghdr msg; + struct nlmsghdr *nlm; + bool done = false; + + // + // We first setup stuff before we start polling for stats + // + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + qWarning("Unable to open netlink socket (errno %d)", errno); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) + { + qWarning("Unable to bind netlink socket (errno %d)", errno); + return -1; + } + + memset(&ifListReq, 0, sizeof(ifListReq)); + ifListReq.nlh.nlmsg_len = sizeof(ifListReq); + ifListReq.nlh.nlmsg_type = RTM_GETLINK; + ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifListReq.nlh.nlmsg_pid = 0; + ifListReq.rtg.rtgen_family = AF_PACKET; + + buf.fill('\0', 1024); + + msg.msg_name = &kernel; + msg.msg_namelen = sizeof(kernel); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); + + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + return -1; + } + + // Find required size of buffer and resize accordingly + while (1) + { + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + msg.msg_flags = 0; + + // Peek at reply to check buffer size required + len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink closed the socket on my face!"); + return -1; + } + else + { + if (msg.msg_flags & MSG_TRUNC) + { + if (len == buf.size()) // Older Kernel returns truncated size + { + qDebug("netlink buffer size %d not enough", buf.size()); + qDebug("retrying with double the size"); + // Double the size and retry + buf.resize(buf.size()*2); + continue; + } + else // Newer Kernel returns actual size required + { + qDebug("netlink required buffer size = %d", len); + buf.resize(len); + continue; + } + } + else + qDebug("buffer size %d enough for netlink", buf.size()); + + break; + } + } + + count = 0; + +_retry: + msg.msg_flags = 0; + + // Actually receive the reply now + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry; + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + return -1; + } + + // + // Populate the port stats hash table + // + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + char ifname[64] = ""; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error %d", err->error); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == IFLA_IFNAME) + { + strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); + ifname[RTA_PAYLOAD(rta)] = 0; + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + + qDebug("if: %s(%d)", ifname, ifi->ifi_index); + foreach(LinuxPort* port, allPorts_) + { + if (strcmp(port->name(), ifname) == 0) + { + portStats[uint(ifi->ifi_index)] = &(port->stats_); + linkState[uint(ifi->ifi_index)] = &(port->linkState_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + count++; + break; + } + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry; + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); + return - 1; + } + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + goto _try_later; + } + + done = false; + +_retry_recv: + msg.msg_flags = 0; + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry_recv; + qWarning("netlink recv error %d", errno); + break; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + break; + } + + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error: %s", strerror(-err->error)); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + // TODO: IFLA_STATS64 + if (rta->rta_type == IFLA_STATS) + { + struct rtnl_link_stats *rtnlStats = + (struct rtnl_link_stats*) RTA_DATA(rta); + AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + OstProto::LinkState *state = linkState[ifi->ifi_index]; + + if (!stats) + break; + + stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts) + / kRefreshFreq_; + stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes) + / kRefreshFreq_; + stats->rxPkts = rtnlStats->rx_packets; + stats->rxBytes = rtnlStats->rx_bytes; + stats->txPps = (rtnlStats->tx_packets - stats->txPkts) + / kRefreshFreq_; + stats->txBps = (rtnlStats->tx_bytes - stats->txBytes) + / kRefreshFreq_; + stats->txPkts = rtnlStats->tx_packets; + stats->txBytes = rtnlStats->tx_bytes; + + // TODO: export detailed error stats + stats->rxDrops = rtnlStats->rx_dropped + + rtnlStats->rx_missed_errors; + stats->rxErrors = rtnlStats->rx_errors; + stats->rxFifoErrors = rtnlStats->rx_fifo_errors; + stats->rxFrameErrors = rtnlStats->rx_crc_errors + + rtnlStats->rx_length_errors + + rtnlStats->rx_over_errors + + rtnlStats->rx_frame_errors; + + Q_ASSERT(state); + *state = ifi->ifi_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; + + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry_recv; + +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); + + return 0; +} + +int LinuxPort::StatsMonitor::setPromisc(const char * portName) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); + + if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) + { + return 1; + } + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + portName, strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + portName, strerror(errno)); + } + + return 0; +} + void LinuxPort::StatsMonitor::stop() { stop_ = true; } + +bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} #endif diff --git a/server/linuxport.h b/server/linuxport.h index 817ece1..2658560 100644 --- a/server/linuxport.h +++ b/server/linuxport.h @@ -43,13 +43,22 @@ protected: { public: StatsMonitor(); + ~StatsMonitor(); void run(); void stop(); + bool waitForSetupFinished(int msecs = 10000); private: + int netlinkStats(); + void procStats(); + int setPromisc(const char* portName); + static const int kRefreshFreq_ = 1; // in seconds bool stop_; + bool setupDone_; + int ioctlSocket_; }; + bool isPromisc_; bool clearPromisc_; static QList allPorts_; static StatsMonitor *monitor_; // rx/tx stats for ALL ports From 14721478a41f93d1b991696ead117d232c4d1579 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Apr 2012 23:35:01 +0530 Subject: [PATCH 200/294] Corrected stats calculation to take care of counter roll over Fixes issue 67 --- server/abstractport.cpp | 33 +++++++++++++++++------ server/abstractport.h | 1 + server/bsdport.cpp | 34 +++++++++++++++++------- server/linuxport.cpp | 58 ++++++++++++++++++++++++++++++++--------- 4 files changed, 97 insertions(+), 29 deletions(-) diff --git a/server/abstractport.cpp b/server/abstractport.cpp index d121bcf..4aa97c6 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -46,6 +46,7 @@ AbstractPort::AbstractPort(int id, const char *device) linkState_ = OstProto::LinkStateUnknown; minPacketSetSize_ = 1; + maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats memset((void*) &stats_, 0, sizeof(stats_)); resetStats(); } @@ -560,18 +561,34 @@ void AbstractPort::updatePacketListInterleaved() void AbstractPort::stats(PortStats *stats) { - stats->rxPkts = stats_.rxPkts - epochStats_.rxPkts; - stats->rxBytes = stats_.rxBytes - epochStats_.rxBytes; + stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? + stats_.rxPkts - epochStats_.rxPkts : + stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); + stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? + stats_.rxBytes - epochStats_.rxBytes : + stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); stats->rxPps = stats_.rxPps; stats->rxBps = stats_.rxBps; - stats->txPkts = stats_.txPkts - epochStats_.txPkts; - stats->txBytes = stats_.txBytes - epochStats_.txBytes; + stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? + stats_.txPkts - epochStats_.txPkts : + stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); + stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? + stats_.txBytes - epochStats_.txBytes : + stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); stats->txPps = stats_.txPps; stats->txBps = stats_.txBps; - stats->rxDrops = stats_.rxDrops - epochStats_.rxDrops; - stats->rxErrors = stats_.rxErrors - epochStats_.rxErrors; - stats->rxFifoErrors = stats_.rxFifoErrors - epochStats_.rxFifoErrors; - stats->rxFrameErrors = stats_.rxFrameErrors - epochStats_.rxFrameErrors; + stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? + stats_.rxDrops - epochStats_.rxDrops : + stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); + stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? + stats_.rxErrors - epochStats_.rxErrors : + stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); + stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? + stats_.rxFifoErrors - epochStats_.rxFifoErrors : + stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); + stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? + stats_.rxFrameErrors - epochStats_.rxFrameErrors : + stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); } diff --git a/server/abstractport.h b/server/abstractport.h index b9c7a6e..44f0c1d 100644 --- a/server/abstractport.h +++ b/server/abstractport.h @@ -107,6 +107,7 @@ protected: OstProto::LinkState linkState_; ulong minPacketSetSize_; + quint64 maxStatsValue_; struct PortStats stats_; //! \todo Need lock for stats access/update diff --git a/server/bsdport.cpp b/server/bsdport.cpp index 2382bcb..3229fc3 100644 --- a/server/bsdport.cpp +++ b/server/bsdport.cpp @@ -63,6 +63,8 @@ BsdPort::BsdPort(int id, const char *device) qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); + + maxStatsValue_ = ULONG_MAX; } BsdPort::~BsdPort() @@ -278,6 +280,7 @@ void BsdPort::StatsMonitor::run() { struct if_data *ifd = &(ifm->ifm_data); OstProto::LinkState *state = linkState[ifm->ifm_index]; + u_long in_packets; Q_ASSERT(state); #ifdef Q_OS_MAC @@ -287,16 +290,29 @@ void BsdPort::StatsMonitor::run() *state = (OstProto::LinkState) ifd->ifi_link_state; #endif - stats->rxPps = (ifd->ifi_ipackets + ifd->ifi_noproto - - stats->rxPkts) /kRefreshFreq_; - stats->rxBps = (ifd->ifi_ibytes - stats->rxBytes) - /kRefreshFreq_; - stats->rxPkts = ifd->ifi_ipackets + ifd->ifi_noproto; + in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxPps = + ((in_packets >= stats->rxPkts) ? + in_packets - stats_->rxPkts : + in_packets + (maxStatsValue_ - stats_->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((ifd->ifi_ibytes >= stats->rxBytes) > + ifd->ifi_ibytes - stats->rxBytes : + ifd->ifi_ibytes + (maxStatsValue_ - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = in_packets; stats->rxBytes = ifd->ifi_ibytes; - stats->txPps = (ifd->ifi_opackets - stats->txPkts) - /kRefreshFreq_; - stats->txBps = (ifd->ifi_obytes - stats->txBytes) - /kRefreshFreq_; + stats->txPps = + ((ifd->ifi_opackets >= stats->rxPkts) > + ifd->ifi_opackets - stats->rxPkts : + ifd->ifi_opackets + (maxStatsValue_ - stats->rxPkts)) + / kRefreshFreq_; + stats->txBps = + ((ifd->ifi_obytes >= stats->rxBytes) > + ifd->ifi_obytes - stats->rxBytes : + ifd->ifi_obytes + (maxStatsValue_ - stats->rxBytes)) + / kRefreshFreq_; stats->txPkts = ifd->ifi_opackets; stats->txBytes = ifd->ifi_obytes; diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 74e7c46..11d09cf 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -58,6 +58,8 @@ LinuxPort::LinuxPort(int id, const char *device) qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); + + maxStatsValue_ = 0xffffffff; } LinuxPort::~LinuxPort() @@ -327,12 +329,28 @@ void LinuxPort::StatsMonitor::procStats() AbstractPort::PortStats *stats = portStats[index]; if (stats) { - stats->rxPps = (rxPkts - stats->rxPkts)/kRefreshFreq_; - stats->rxBps = (rxBytes - stats->rxBytes)/kRefreshFreq_; + stats->rxPps = + ((rxPkts >= stats->rxPkts) ? + rxPkts - stats->rxPkts : + rxPkts + (maxStatsValue_ - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rxBytes >= stats->rxBytes) ? + rxBytes - stats->rxBytes : + rxBytes + (maxStatsValue_ - stats->rxBytes)) + / kRefreshFreq_; stats->rxPkts = rxPkts; stats->rxBytes = rxBytes; - stats->txPps = (txPkts - stats->txPkts)/kRefreshFreq_; - stats->txBps = (txBytes - stats->txBytes)/kRefreshFreq_; + stats->txPps = + ((txPkts >= stats->txPkts) ? + txPkts - stats->txPkts : + txPkts + (maxStatsValue_ - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((txBytes >= stats->txBytes) ? + txBytes - stats->txBytes : + txBytes + (maxStatsValue_ - stats->txBytes)) + / kRefreshFreq_; stats->txPkts = txPkts; stats->txBytes = txBytes; @@ -629,16 +647,32 @@ _retry_recv: if (!stats) break; - stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts) - / kRefreshFreq_; - stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes) - / kRefreshFreq_; + stats->rxPps = + ((rtnlStats->rx_packets >= stats->rxPkts) ? + rtnlStats->rx_packets - stats->rxPkts : + rtnlStats->rx_packets + (maxStatsValue_ + - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rtnlStats->rx_bytes >= stats->rxBytes) ? + rtnlStats->rx_bytes - stats->rxBytes : + rtnlStats->rx_bytes + (maxStatsValue_ + - stats->rxBytes)) + / kRefreshFreq_; stats->rxPkts = rtnlStats->rx_packets; stats->rxBytes = rtnlStats->rx_bytes; - stats->txPps = (rtnlStats->tx_packets - stats->txPkts) - / kRefreshFreq_; - stats->txBps = (rtnlStats->tx_bytes - stats->txBytes) - / kRefreshFreq_; + stats->txPps = + ((rtnlStats->tx_packets >= stats->txPkts) ? + rtnlStats->tx_packets - stats->txPkts : + rtnlStats->tx_packets + (maxStatsValue_ + - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((rtnlStats->tx_bytes >= stats->txBytes) ? + rtnlStats->tx_bytes - stats->txBytes : + rtnlStats->tx_bytes + (maxStatsValue_ + - stats->txBytes)) + / kRefreshFreq_; stats->txPkts = rtnlStats->tx_packets; stats->txBytes = rtnlStats->tx_bytes; From 64a82f9b2c0f49bb70cf8a12f2064ff58cefe50f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 15 Apr 2012 13:30:36 +0530 Subject: [PATCH 201/294] Fixed linux compilation issue --- server/linuxport.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 11d09cf..b5f7178 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -38,6 +38,8 @@ along with this program. If not, see QList LinuxPort::allPorts_; LinuxPort::StatsMonitor *LinuxPort::monitor_; +const quint32 kMaxValue32 = 0xffffffff; + LinuxPort::LinuxPort(int id, const char *device) : PcapPort(id, device) { @@ -332,24 +334,24 @@ void LinuxPort::StatsMonitor::procStats() stats->rxPps = ((rxPkts >= stats->rxPkts) ? rxPkts - stats->rxPkts : - rxPkts + (maxStatsValue_ - stats->rxPkts)) + rxPkts + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->rxBps = ((rxBytes >= stats->rxBytes) ? rxBytes - stats->rxBytes : - rxBytes + (maxStatsValue_ - stats->rxBytes)) + rxBytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->rxPkts = rxPkts; stats->rxBytes = rxBytes; stats->txPps = ((txPkts >= stats->txPkts) ? txPkts - stats->txPkts : - txPkts + (maxStatsValue_ - stats->txPkts)) + txPkts + (kMaxValue32 - stats->txPkts)) / kRefreshFreq_; stats->txBps = ((txBytes >= stats->txBytes) ? txBytes - stats->txBytes : - txBytes + (maxStatsValue_ - stats->txBytes)) + txBytes + (kMaxValue32 - stats->txBytes)) / kRefreshFreq_; stats->txPkts = txPkts; stats->txBytes = txBytes; @@ -650,13 +652,13 @@ _retry_recv: stats->rxPps = ((rtnlStats->rx_packets >= stats->rxPkts) ? rtnlStats->rx_packets - stats->rxPkts : - rtnlStats->rx_packets + (maxStatsValue_ + rtnlStats->rx_packets + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->rxBps = ((rtnlStats->rx_bytes >= stats->rxBytes) ? rtnlStats->rx_bytes - stats->rxBytes : - rtnlStats->rx_bytes + (maxStatsValue_ + rtnlStats->rx_bytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->rxPkts = rtnlStats->rx_packets; @@ -664,13 +666,13 @@ _retry_recv: stats->txPps = ((rtnlStats->tx_packets >= stats->txPkts) ? rtnlStats->tx_packets - stats->txPkts : - rtnlStats->tx_packets + (maxStatsValue_ + rtnlStats->tx_packets + (kMaxValue32 - stats->txPkts)) / kRefreshFreq_; stats->txBps = ((rtnlStats->tx_bytes >= stats->txBytes) ? rtnlStats->tx_bytes - stats->txBytes : - rtnlStats->tx_bytes + (maxStatsValue_ + rtnlStats->tx_bytes + (kMaxValue32 - stats->txBytes)) / kRefreshFreq_; stats->txPkts = rtnlStats->tx_packets; From 393124546308538b39dad647a87cd2d2615a3134 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 15 Apr 2012 20:32:13 +0530 Subject: [PATCH 202/294] Fixed BSD/OSX compilation issue --- server/bsdport.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/bsdport.cpp b/server/bsdport.cpp index 3229fc3..39c2974 100644 --- a/server/bsdport.cpp +++ b/server/bsdport.cpp @@ -43,6 +43,8 @@ along with this program. If not, see QList BsdPort::allPorts_; BsdPort::StatsMonitor *BsdPort::monitor_; +const quint32 kMaxValue32 = 0xffffffff; + BsdPort::BsdPort(int id, const char *device) : PcapPort(id, device) { @@ -293,25 +295,25 @@ void BsdPort::StatsMonitor::run() in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; stats->rxPps = ((in_packets >= stats->rxPkts) ? - in_packets - stats_->rxPkts : - in_packets + (maxStatsValue_ - stats_->rxPkts)) + in_packets - stats->rxPkts : + in_packets + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->rxBps = - ((ifd->ifi_ibytes >= stats->rxBytes) > + ((ifd->ifi_ibytes >= stats->rxBytes) ? ifd->ifi_ibytes - stats->rxBytes : - ifd->ifi_ibytes + (maxStatsValue_ - stats->rxBytes)) + ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->rxPkts = in_packets; stats->rxBytes = ifd->ifi_ibytes; stats->txPps = - ((ifd->ifi_opackets >= stats->rxPkts) > + ((ifd->ifi_opackets >= stats->rxPkts) ? ifd->ifi_opackets - stats->rxPkts : - ifd->ifi_opackets + (maxStatsValue_ - stats->rxPkts)) + ifd->ifi_opackets + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->txBps = - ((ifd->ifi_obytes >= stats->rxBytes) > + ((ifd->ifi_obytes >= stats->rxBytes) ? ifd->ifi_obytes - stats->rxBytes : - ifd->ifi_obytes + (maxStatsValue_ - stats->rxBytes)) + ifd->ifi_obytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->txPkts = ifd->ifi_opackets; stats->txBytes = ifd->ifi_obytes; From 19424d980c051ecfbbd89f8693ed9cb28c16cbfc Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 4 Jun 2012 21:46:57 +0530 Subject: [PATCH 203/294] Added missing include for unistd.h since gcc 4.7 refuses to compile without it --- server/linuxport.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/server/linuxport.cpp b/server/linuxport.cpp index b5f7178..a04df1e 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -32,6 +32,7 @@ along with this program. If not, see #include #include #include +#include #include From f7d8cc2684892dc8555eee2b5eebb13c14d7e9e9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 22 Jul 2012 13:26:52 +0530 Subject: [PATCH 204/294] Fixed rx/tx rate stats on BSP/OSX --- server/bsdport.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/bsdport.cpp b/server/bsdport.cpp index 39c2974..4ac9ab7 100644 --- a/server/bsdport.cpp +++ b/server/bsdport.cpp @@ -306,14 +306,14 @@ void BsdPort::StatsMonitor::run() stats->rxPkts = in_packets; stats->rxBytes = ifd->ifi_ibytes; stats->txPps = - ((ifd->ifi_opackets >= stats->rxPkts) ? - ifd->ifi_opackets - stats->rxPkts : - ifd->ifi_opackets + (kMaxValue32 - stats->rxPkts)) + ((ifd->ifi_opackets >= stats->txPkts) ? + ifd->ifi_opackets - stats->txPkts : + ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) / kRefreshFreq_; stats->txBps = - ((ifd->ifi_obytes >= stats->rxBytes) ? - ifd->ifi_obytes - stats->rxBytes : - ifd->ifi_obytes + (kMaxValue32 - stats->rxBytes)) + ((ifd->ifi_obytes >= stats->txBytes) ? + ifd->ifi_obytes - stats->txBytes : + ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) / kRefreshFreq_; stats->txPkts = ifd->ifi_opackets; stats->txBytes = ifd->ifi_obytes; From 1d5ae64281f25933c657fee6a7e6353a48d59e85 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 30 Jul 2012 18:53:42 +0530 Subject: [PATCH 205/294] Version bump to 0.5.1 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index 8eb45f0..718ce69 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.5 +APP_VERSION = 0.5.1 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 53437d5216223a4e34348ef7e64be34dcfed2d72 Mon Sep 17 00:00:00 2001 From: Shlomo Shachar Date: Tue, 1 Jan 2013 15:23:06 +0200 Subject: [PATCH 206/294] Fix bug in LinuxPort::StatsMonitor::netlinkStats() causing some ports not to be updated on system with many ports. Problem occurs as the netlink msg size is calculated once and then received multiple times. The following commit makes sure the netlink msg size is recalcaulted before each recv. Fixes issue 94 --- server/linuxport.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/linuxport.cpp b/server/linuxport.cpp index a04df1e..18cf525 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -437,6 +437,10 @@ int LinuxPort::StatsMonitor::netlinkStats() return -1; } + count = 0; + +_retry: + // Find required size of buffer and resize accordingly while (1) { @@ -486,9 +490,6 @@ int LinuxPort::StatsMonitor::netlinkStats() } } - count = 0; - -_retry: msg.msg_flags = 0; // Actually receive the reply now From 87e24af3b1c635e607b98bc69642fece173f4ac4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 25 Feb 2013 22:20:50 +0530 Subject: [PATCH 207/294] Ports in the PortStatsWindow should be named by their IDs instead of their indices. This becomes evident when multiple drones (portgroups) are connected and the one with a lower id (and hence index) is deleted --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 +++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 88 ++ client/mainwindow.cpp | 135 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 +++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 88 ++ client/ostinato.qrc | 38 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 573 ++++++++++ client/port.h | 147 +++ client/portconfigdialog.cpp | 57 + client/portconfigdialog.h | 39 + client/portconfigdialog.ui | 109 ++ client/portgroup.cpp | 838 ++++++++++++++ client/portgroup.h | 145 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 339 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 +++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 326 ++++++ client/portstatsmodel.h | 151 +++ client/portstatswindow.cpp | 180 +++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 +++ client/portswindow.cpp | 663 +++++++++++ client/portswindow.h | 86 ++ client/portswindow.ui | 299 +++++ client/preferences.cpp | 119 ++ client/preferences.h | 45 + client/preferences.ui | 226 ++++ client/settings.h | 86 ++ client/stream.cpp | 79 ++ client/stream.h | 42 + client/streamconfigdialog.cpp | 1143 +++++++++++++++++++ client/streamconfigdialog.h | 146 +++ client/streamconfigdialog.ui | 1462 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 288 +++++ client/streammodel.h | 78 ++ common/abstractfileformat.cpp | 127 +++ common/abstractfileformat.h | 91 ++ common/abstractprotocol.cpp | 928 ++++++++++++++++ common/abstractprotocol.h | 167 +++ common/arp.cpp | 993 +++++++++++++++++ common/arp.h | 121 ++ common/arp.proto | 67 ++ common/arp.ui | 518 +++++++++ common/comboprotocol.h | 223 ++++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot3.cpp | 239 ++++ common/dot3.h | 80 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/eth2.cpp | 231 ++++ common/eth2.h | 78 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/fileformat.cpp | 483 ++++++++ common/fileformat.h | 62 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 1055 ++++++++++++++++++ common/gmp.h | 163 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 ++++++++++++++ common/hexdump.cpp | 263 +++++ common/hexdump.h | 89 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/icmp.cpp | 594 ++++++++++ common/icmp.h | 117 ++ common/icmp.proto | 44 + common/icmp.ui | 178 +++ common/igmp.cpp | 449 ++++++++ common/igmp.h | 111 ++ common/igmp.proto | 27 + common/intcombobox.h | 69 ++ common/ip4.cpp | 752 +++++++++++++ common/ip4.h | 116 ++ common/ip4.proto | 66 ++ common/ip4.ui | 516 +++++++++ common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip6.cpp | 953 ++++++++++++++++ common/ip6.h | 131 +++ common/ip6.proto | 61 ++ common/ip6.ui | 467 ++++++++ common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/iputils.h | 122 +++ common/ipv4addressdelegate.h | 58 + common/ipv6addressdelegate.h | 60 + common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 331 ++++++ common/llc.h | 86 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/mac.cpp | 329 ++++++ common/mac.h | 91 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/mld.cpp | 624 +++++++++++ common/mld.h | 108 ++ common/mld.proto | 27 + common/ostproto.pro | 142 +++ common/ostprotolib.cpp | 55 + common/ostprotolib.h | 42 + common/payload.cpp | 284 +++++ common/payload.h | 85 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/pcapfileformat.cpp | 661 +++++++++++ common/pcapfileformat.h | 87 ++ common/pcapfileimport.ui | 132 +++ common/pdmlfileformat.cpp | 163 +++ common/pdmlfileformat.h | 42 + common/pdmlprotocol.cpp | 153 +++ common/pdmlprotocol.h | 64 ++ common/pdmlprotocols.cpp | 1357 +++++++++++++++++++++++ common/pdmlprotocols.h | 313 ++++++ common/pdmlreader.cpp | 533 +++++++++ common/pdmlreader.h | 75 ++ common/protocol.proto | 249 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 212 ++++ common/protocolmanager.h | 57 + common/sample.cpp | 546 +++++++++ common/sample.h | 103 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/snap.cpp | 307 ++++++ common/snap.h | 82 ++ common/snap.proto | 34 + common/snap.ui | 122 +++ common/streambase.cpp | 565 ++++++++++ common/streambase.h | 150 +++ common/svlan.cpp | 68 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/tcp.cpp | 709 ++++++++++++ common/tcp.h | 101 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/textproto.cpp | 295 +++++ common/textproto.h | 91 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/udp.cpp | 500 +++++++++ common/udp.h | 88 ++ common/udp.proto | 39 + common/udp.ui | 174 +++ common/userscript.cpp | 630 +++++++++++ common/userscript.h | 190 ++++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/vlan.cpp | 257 +++++ common/vlan.h | 82 ++ common/vlan.proto | 34 + common/vlan.ui | 179 +++ common/vlanstack.h | 30 + common/vlanstack.proto | 31 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 12 + extra/qhexedit2/src/commands.cpp | 115 ++ extra/qhexedit2/src/commands.h | 70 ++ extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 152 +++ extra/qhexedit2/src/qhexedit.h | 205 ++++ extra/qhexedit2/src/qhexedit_p.cpp | 800 ++++++++++++++ extra/qhexedit2/src/qhexedit_p.h | 120 ++ extra/qhexedit2/src/xbytearray.cpp | 167 +++ extra/qhexedit2/src/xbytearray.h | 66 ++ install.pri | 14 + ost.pro | 8 + protobuf.pri | 33 + rpc/pbhelper.h | 170 +++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 338 ++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 39 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 291 +++++ rpc/rpcserver.h | 66 ++ server/abstractport.cpp | 594 ++++++++++ server/abstractport.h | 127 +++ server/bsdport.cpp | 355 ++++++ server/bsdport.h | 61 ++ server/drone.cpp | 102 ++ server/drone.h | 56 + server/drone.pro | 48 + server/drone.qrc | 5 + server/drone.ui | 190 ++++ server/drone_main.cpp | 81 ++ server/icons/portgroup.png | Bin 0 -> 667 bytes server/linuxport.cpp | 769 +++++++++++++ server/linuxport.h | 68 ++ server/myservice.cpp | 475 ++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 788 +++++++++++++ server/pcapport.h | 217 ++++ server/portmanager.cpp | 94 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 226 ++++ server/winpcapport.h | 56 + test/main.cpp | 109 ++ test/test.pro | 34 + version.pri | 19 + 273 files changed, 46211 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portconfigdialog.cpp create mode 100644 client/portconfigdialog.h create mode 100644 client/portconfigdialog.ui create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/comboprotocol.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/ostproto.pro create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h create mode 100644 common/pcapfileimport.ui create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlprotocols.cpp create mode 100644 common/pdmlprotocols.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/commands.cpp create mode 100644 extra/qhexedit2/src/commands.h create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 extra/qhexedit2/src/xbytearray.cpp create mode 100644 extra/qhexedit2/src/xbytearray.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/bsdport.cpp create mode 100644 server/bsdport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone.qrc create mode 100644 server/drone.ui create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/linuxport.cpp create mode 100644 server/linuxport.h create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 test/main.cpp create mode 100644 test/test.pro create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..4727324 --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2012 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
    G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/ostprotolib.h" +#include "../common/protocolmanager.h" +#include "settings.h" + +#include +#include +#include + +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; +QMainWindow *mainWindow; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + + delete mainWindow; + delete appSettings; + delete OstProtocolManager; + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..03a16ab --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,135 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "settings.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); + statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..0967390 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,88 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portconfigdialog.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portconfigdialog.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portconfigdialog.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..6162ac0 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,38 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/gaps.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..7f1b4c9 --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,573 @@ +/* +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 +*/ + +#include "port.h" + +#include "abstractfileformat.h" + +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; + +uint Port::mAllocStreamId = 0; + +static const int kEthOverhead = 20; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +void Port::recalculateAverageRates() +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAveragePacketRate(double packetsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) * + (packetsPerSec - avgPacketsPerSec_)); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); +} + +void Port::setAverageBitRate(double bitsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); + qDebug("rate = %g", rate); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) + * ((bitsPerSec - avgBitsPerSec_) + / ((s->frameLenAvg()+kEthOverhead)*8))); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + emit portRateChanged(mPortGroupId, mPortId); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + bool ret = false; + QDialog *optDialog; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); + OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); + + if (fmt == NULL) + goto _fail; + + if ((optDialog = fmt->openOptionsDialog())) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) + goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); + + if (!append) + { + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + } + + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); + for (int i = 0; i < streams.stream_size(); i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + +_user_cancel: + emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: + ret = true; + +_fail: + progress.close(); + mainWindow->setEnabled(true); + recalculateAverageRates(); + return ret; +} + +bool Port::saveStreams(QString fileName, QString fileType, QString &error) +{ + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); + OstProto::StreamConfigList streams; + + if (fmt == NULL) + goto _fail; + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..10c2803 --- /dev/null +++ b/client/port.h @@ -0,0 +1,147 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + double avgPacketsPerSec_; + double avgBitsPerSec_; + int numActiveStreams_; + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + OstProto::TransmitMode transmitMode() + { return d.transmit_mode(); } + double averagePacketRate() + { return avgPacketsPerSec_; } + double averageBitRate() + { return avgBitsPerSec_; } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void setAveragePacketRate(double packetsPerSec); + void setAverageBitRate(double bitsPerSec); + // FIXME(MED): Bad Hack! port should not need an external trigger to + // recalculate - refactor client side domain objects and model objects + void recalculateAverageRates(); + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); + +signals: + void portRateChanged(int portGroupId, int portId); + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portconfigdialog.cpp b/client/portconfigdialog.cpp new file mode 100644 index 0000000..17adeb5 --- /dev/null +++ b/client/portconfigdialog.cpp @@ -0,0 +1,57 @@ +/* +Copyright (C) 2011 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 "portconfigdialog.h" + +PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent) + : QDialog(parent), portConfig_(portConfig) +{ + qDebug("In %s", __FUNCTION__); + + setupUi(this); + + switch(portConfig_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + sequentialStreamsButton->setChecked(true); + break; + case OstProto::kInterleavedTransmit: + interleavedStreamsButton->setChecked(true); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } + + exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); +} + +void PortConfigDialog::accept() +{ + if (sequentialStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kSequentialTransmit); + else if (interleavedStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit); + else + Q_ASSERT(false); // Unreachable!!! + + portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked()); + + QDialog::accept(); +} diff --git a/client/portconfigdialog.h b/client/portconfigdialog.h new file mode 100644 index 0000000..4d14b0b --- /dev/null +++ b/client/portconfigdialog.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PORT_CONFIG_DIALOG_H +#define _PORT_CONFIG_DIALOG_H + +#include "ui_portconfigdialog.h" +#include "protocol.pb.h" +#include + +class PortConfigDialog : public QDialog, public Ui::PortConfigDialog +{ +public: + PortConfigDialog(OstProto::Port &portConfig, QWidget *parent); + +private: + virtual void accept(); + + OstProto::Port &portConfig_; +}; + +#endif + diff --git a/client/portconfigdialog.ui b/client/portconfigdialog.ui new file mode 100644 index 0000000..954b827 --- /dev/null +++ b/client/portconfigdialog.ui @@ -0,0 +1,109 @@ + + PortConfigDialog + + + + 0 + 0 + 244 + 160 + + + + Port Config + + + + + + Transmit Mode + + + + + + Sequential Streams + + + true + + + + + + + Interleaved Streams + + + + + + + + + + Exclusive Control + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PortConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..9762fac --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,838 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://code.google.com/p/ostinato/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->CopyFrom(portConfig); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + + emit portGroupDataChanged(mPortGroupId, id); + } + + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..b4d7580 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,145 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { reconnect = true; rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, OstProto::Port portConfig); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..0ef055e --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,339 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + qDebug("portGroupId = %d, portId = %d", portGroupId, portId); + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..cd53d4d --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,326 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + case e_STAT_RX_DROPS : return quint64(stats.rx_drops()); + case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); + case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); + case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors()); + + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2") + .arg(pgl->mPortGroups.at(portGroupIdx)->id()) + .arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id()); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..a0d6868 --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + // Rx Errors + e_STAT_RX_DROPS, + e_STAT_RX_ERRORS, + e_STAT_RX_FIFO_ERRORS, + e_STAT_RX_FRAME_ERRORS, + + e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif + << "Receive Drops" + << "Receive Errors" + << "Receive Fifo Errors" + << "Receive Frame Errors" +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..1f3bc4d --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,663 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include "abstractfileformat.h" +#include "portconfigdialog.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +#include +#include +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + tvPortList->addAction(actionPort_Configuration); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + connect(plm->getStreamModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(streamModelDataChanged())); + connect(plm->getStreamModel(), + SIGNAL(modelReset()), + this, SLOT(streamModelDataChanged())); +} + +void PortsWindow::streamModelDataChanged() +{ + if (plm->isPort(tvPortList->currentIndex())) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + int ret; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + ret = scd->exec(); + + if (ret == QDialog::Accepted) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); + + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + qDebug("In %s", __FUNCTION__); + + if (previous.isValid() && plm->isPort(previous)) + { + disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), + this, SLOT(updatePortRates())); + } + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + updatePortRates(); + connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), + SLOT(updatePortRates())); + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + qDebug("In %s", __FUNCTION__); +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + // Update UI to reflect potential change in exclusive mode, + // transmit mode et al + when_portView_currentChanged(tvPortList->currentIndex(), + tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::on_averagePacketsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); + + plm->port(current).setAveragePacketRate(pps); +} + +void PortsWindow::on_averageBitsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); + + plm->port(current).setAverageBitRate(bps); +} + +void PortsWindow::updatePortRates() +{ + QModelIndex current = tvPortList->currentIndex(); + + if (!current.isValid()) + return; + + if (!plm->isPort(current)) + return; + + averagePacketsPerSec->setText(QString("%L1") + .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); + averageBitsPerSec->setText(QString("%L1") + .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + actionPort_Configuration->setEnabled(true); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + { + OstProto::Port config; + + config.set_is_exclusive_control(checked); + plm->portGroup(current.parent()).modifyPort(current.row(), config); + } +} + +void PortsWindow::on_actionPort_Configuration_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (!plm->isPort(current)) + return; + + OstProto::Port config; + config.set_transmit_mode(plm->port(current).transmitMode()); + config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); + + PortConfigDialog dialog(config, this); + + if (dialog.exec() == QDialog::Accepted) + plm->portGroup(current.parent()).modifyPort(current.row(), config); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; + QString fileName; + QString errorStr; + bool append = true; + bool ret; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + dirName = QFileInfo(fileName).absolutePath(); + +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; + QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need.On some Linux distros the native dialog can't + // distinguish between Ostinato(*) and PCAP(*) +#if defined(Q_OS_MAC) || defined(Q_OS_UNIX) + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); + + Q_ASSERT(plm->isPort(current)); + +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); + if (fileName.isEmpty()) + goto _exit; + + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..5c071f0 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_averagePacketsPerSec_editingFinished(); + void on_averageBitsPerSec_editingFinished(); + void updatePortRates(); + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + void on_actionPort_Configuration_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); + + void streamModelDataChanged(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a5d9261 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,299 @@ + + PortsWindow + + + + 0 + 0 + 710 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Avg pps + + + true + + + + + + + + + + Avg bps + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 1 + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + Port Configuration ... + + + + + + + + + radioButton + toggled(bool) + averagePacketsPerSec + setEnabled(bool) + + + 313 + 28 + + + 380 + 28 + + + + + radioButton_2 + toggled(bool) + averageBitsPerSec + setEnabled(bool) + + + 333 + 55 + + + 395 + 56 + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..0e54bbd --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,119 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "../common/ostprotolib.h" +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..78109ab --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..d64b4cb --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,226 @@ + + Preferences + + + + 0 + 0 + 400 + 220 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 'wireshark' Path + + + wiresharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + Qt::Vertical + + + + 21 + 61 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..d76bb47 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..a24c971 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,79 @@ +/* +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 +*/ + +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..e2fc3f0 --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1143 @@ +/* +Copyright (C) 2010-2011 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 + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager *OstProtocolManager; + +QRect StreamConfigDialog::lastGeometry; +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; + +static const uint kEthFrameOverHead = 20; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setHidden(true); + pbNext->setHidden(true); + //! \todo Support Goto Stream Id + leStreamId->setHidden(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + + switch(mPort.transmitMode()) + { + case OstProto::kSequentialTransmit: + rbModeFixed->setChecked(true); + rbModeContinuous->setDisabled(true); + break; + case OstProto::kInterleavedTransmit: + rbModeContinuous->setChecked(true); + rbModeFixed->setDisabled(true); + + nextWhat->setHidden(true); + break; + default: + Q_ASSERT(false); // Unreachable + } + + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); + + tbProtocolData->show(); + break; + } + + // Stream Control + case 2: + { + StoreCurrentStream(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText( + QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); + leBurstsPerSec->setText( + QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + + leGapIsg->setText("0.0"); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate( + QLocale().toDouble(lePacketsPerSec->text(), &isOk)); + pStream->setBurstRate( + QLocale().toDouble(leBurstsPerSec->text(), &isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) +{ + if (checked) + on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); +} + +void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) +{ + if (checked) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) +{ + if (rbSendBursts->isChecked()) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); + + if (rbPacketsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("0.0")); + leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); + } +} + +void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = burstsPerSec * + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + if (rbBurstsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); + leGapIpg->setText(QString("0.0")); + } + qDebug("end of %s", __FUNCTION__); +} + +void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk)/ + double((frameLen+kEthFrameOverHead)*8); + lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); + } + else if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk)/ + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); + } +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) + && (mpStream->isFrameVariable())) + { + log += "In 'Interleaved Streams' transmit mode, the count for " + "varying fields at transmit time may not be same as configured\n"; + } + + mpStream->preflightCheck(log); + + if (log.length()) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastGeometry = geometry(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..a3214c3 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,146 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + // "Stream Control" related + void on_rbPacketsPerSec_toggled(bool checked); + void on_rbBurstsPerSec_toggled(bool checked); + + void on_lePacketsPerBurst_textChanged(const QString &text); + void on_lePacketsPerSec_textChanged(const QString &text); + void on_leBurstsPerSec_textChanged(const QString &text); + void on_leBitsPerSec_textEdited(const QString &text); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..be71e47 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1462 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumBursts + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + lePacketsPerBurst + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Bursts/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bits/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto First + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + + 20 + 41 + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + true + + + Gaps (in seconds) + + + false + + + false + + + + + + + + + :/icons/gaps.png + + + + + + + ISG + + + leGapIsg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IBG + + + leGapIbg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leGapIpg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 153 + 21 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
    dumpview.h
    + 1 +
    +
    + + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 623 + 496 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 463 + 143 + + + 463 + 177 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setEnabled(bool) + + + 30 + 68 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setEnabled(bool) + + + 30 + 95 + + + 299 + 132 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 30 + 95 + + + 134 + 189 + + + + + rbPacketsPerSec + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 299 + 82 + + + 299 + 108 + + + + + rbBurstsPerSec + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 299 + 132 + + + 299 + 158 + + + + + rbBitsPerSec + toggled(bool) + leBitsPerSec + setEnabled(bool) + + + 299 + 182 + + + 299 + 208 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setChecked(bool) + + + 95 + 70 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setChecked(bool) + + + 96 + 98 + + + 299 + 132 + + + + + rbModeContinuous + toggled(bool) + leNumPackets + setDisabled(bool) + + + 73 + 196 + + + 164 + 108 + + + + + rbModeContinuous + toggled(bool) + leNumBursts + setDisabled(bool) + + + 96 + 199 + + + 226 + 155 + + + + +
    diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..c66f02c --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,288 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + int count = StreamMaxFields; + if (mCurrentPort && + (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) + count--; + + return count; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..d559618 --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamStatus, + StreamName, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..15271d7 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ + stop_ = false; +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..1f8447d --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class AbstractFileFormat : public QThread +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif + diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..77def8f --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,928 @@ +/* +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 +*/ + +#include + +#include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + - protocolFrameVariableCount() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return (protocolFrameVariableCount() > 1); +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns the minimum number of frames required for the protocol to + vary its fields + + This is the lowest common multiple (LCM) of the counts of all the varying + fields in the protocol. Use the AbstractProtocol::lcm() static utility + function to calculate the LCM. + + The default implementation returns 1 implying that the protocol has no + varying fields. A subclass should reimplement if it has varying fields + e.g. an IP protocol that increments/decrements the IP address with + every packet +*/ +int AbstractProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +int AbstractProtocol::protocolFramePayloadVariableCount() const +{ + int count = 1; + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable() + || p->isProtocolFrameSizeVariable()) + count = lcm(count, p->protocolFrameVariableCount()); + p = p->next; + } + if (parent && (parent->isProtocolFramePayloadValueVariable() + || parent->isProtocolFramePayloadSizeVariable())) + count = lcm(count, parent->protocolFramePayloadVariableCount()); + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +// Stein's binary GCD algo - from wikipedia +quint64 AbstractProtocol::gcd(quint64 u, quint64 v) +{ + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Let shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + quint64 diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +quint64 AbstractProtocol::lcm(quint64 u, quint64 v) +{ +#if 0 + /* LCM(0,x) := x */ + if (u == 0 || v == 0) + return u | v; +#else + /* For our use case, neither u nor v can ever be 0, the minimum + value is 1; we do this correction silently here */ + if (u == 0) u = 1; + if (v == 0) v = 1; + + if (u == 1 || v == 1) + return (u * v); +#endif + + return (u * v)/gcd(u, v); +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..ab3b7e9 --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,167 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; + + static quint64 lcm(quint64 u, quint64 v); + static quint64 gcd(quint64 u, quint64 v); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..84b10a8 --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,993 @@ +/* +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 +*/ + +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + return true; + + return false; +} + +int ArpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); + } + + return count; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..d5582d2 --- /dev/null +++ b/common/arp.h @@ -0,0 +1,121 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
    diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..bc148d3 --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,223 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + virtual int protocolFrameVariableCount() const + { + return AbstractProtocol::lcm( + protoA->protocolFrameVariableCount(), + protoB->protocolFrameVariableCount()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..6b7afb0 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,239 @@ +/* +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 +*/ + +#include "dot3.h" +#include "streambase.h" + +#include +#include +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Dot3Protocol::~Dot3Protocol() +{ + delete configForm; +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +int Dot3Protocol::protocolFrameVariableCount() const +{ + return protocolFramePayloadVariableCount(); +} + +QWidget* Dot3Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideLength->setChecked( + fieldData(dot3_is_override_length, FieldValue).toBool()); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); +} + +void Dot3Protocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(dot3_is_override_length, + configForm->cbOverrideLength->isChecked()); + setFieldData(dot3_length,configForm->leLength->text()); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..22c3f5b --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..73038b7 --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,231 @@ +/* +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 +*/ + +#include +#include + +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Eth2Protocol::~Eth2Protocol() +{ + delete configForm; +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideType->setChecked( + fieldData(eth2_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(eth2_is_override_type, + configForm->cbOverrideType->isChecked()); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5694339 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + +public: + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..4edd980 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,483 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + postParseFixup(meta.data(), content); + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void FileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..409b8a8 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include "fileformat.pb.h" + +class FileFormat : public AbstractFileFormat +{ +public: + FileFormat(); + ~FileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 2; + static const uint kFileFormatVersionRevision = 3; +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..a7b4a3d --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,1055 @@ +/* +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 +*/ + +#include "gmp.h" + +#include +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +GmpProtocol::~GmpProtocol() +{ + delete configForm; +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +int GmpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return count; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(kGroupCount, FieldValue).toUInt()); + } + + return count; +} + +void GmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); + // XXX: configForm->maxResponseTime set by subclass + configForm->overrideChecksum->setChecked( + fieldData(kIsOverrideChecksum, FieldValue).toBool()); + configForm->checksum->setText(uintToHexStr( + fieldData(kChecksum, FieldValue).toUInt(), 2)); + + configForm->groupAddress->setText( + fieldData(kGroupAddress, FieldValue).toString()); + configForm->groupMode->setCurrentIndex( + fieldData(kGroupMode, FieldValue).toUInt()); + configForm->groupCount->setText( + fieldData(kGroupCount, FieldValue).toString()); + configForm->groupPrefix->setText( + fieldData(kGroupPrefix, FieldValue).toString()); + + configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); + configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); + configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + + QStringList sl = fieldData(kSources, FieldValue).toStringList(); + configForm->sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + configForm->sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + configForm->overrideSourceCount->setChecked( + fieldData(kIsOverrideSourceCount, FieldValue).toBool()); + configForm->sourceCount->setText( + fieldData(kSourceCount, FieldValue).toString()); + + QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); + configForm->groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(configForm->groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + configForm->groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + configForm->overrideGroupRecordCount->setChecked( + fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); + configForm->groupRecordCount->setText( + fieldData(kGroupRecordCount, FieldValue).toString()); + +} + +void GmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + configForm->update(); + + setFieldData(kType, configForm->msgTypeCombo->currentValue()); + // XXX: configForm->maxResponseTime handled by subclass + setFieldData(kIsOverrideChecksum, + configForm->overrideChecksum->isChecked()); + setFieldData(kChecksum, + configForm->checksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kGroupAddress, configForm->groupAddress->text()); + setFieldData(kGroupMode, configForm->groupMode->currentIndex()); + setFieldData(kGroupCount, configForm->groupCount->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); + + setFieldData(kSFlag, configForm->sFlag->isChecked()); + setFieldData(kQrv, configForm->qrv->text()); + setFieldData(kQqic, configForm->qqi->text()); + + QStringList list; + for (int i = 0; i < configForm->sourceList->count(); i++) + list.append(configForm->sourceList->item(i)->text()); + setFieldData(kSources, list); + + // sourceCount should be AFTER sources + setFieldData(kIsOverrideSourceCount, + configForm->overrideSourceCount->isChecked()); + setFieldData(kSourceCount, configForm->sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < configForm->groupList->count(); i++) + { + QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + setFieldData(kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + setFieldData(kIsOverrideGroupRecordCount, + configForm->overrideGroupRecordCount->isChecked()); + setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..7f3fd29 --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,163 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "gmp.pb.h" +#include "ui_gmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ +class GmpProtocol; + +class GmpConfigForm : public QWidget, public Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + void update(); +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +class GmpProtocol : public AbstractProtocol +{ +public: + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + OstProto::Gmp data; + GmpConfigForm *configForm; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
    diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..f579430 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,263 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +#include + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +HexDumpProtocol::~HexDumpProtocol() +{ + delete configForm; +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + +QWidget* HexDumpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new HexDumpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void HexDumpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hexEdit->setData( + fieldData(hexDump_content, FieldValue).toByteArray()); + configForm->padUntilEnd->setChecked( + fieldData(hexDump_pad_until_end, FieldValue).toBool()); +} + +void HexDumpProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(hexDump_content, configForm->hexEdit->data()); + setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..f5b6932 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "hexdump.pb.h" +#include "ui_hexdump.h" + +#include "abstractprotocol.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpConfigForm : public QWidget, public Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +class HexDumpProtocol : public AbstractProtocol +{ +public: + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + OstProto::HexDump data; + HexDumpConfigForm *configForm; + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
    qhexedit.h
    + 1 +
    +
    + + +
    diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..e7f6267 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,594 @@ +/* +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 +*/ + + +#include "icmp.h" + +#include +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->versionGroup->button(icmpVersion())->click(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..a3fc296 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,117 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + QButtonGroup *versionGroup; + + IcmpConfigForm(QWidget *parent = 0); +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
    diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..046f675 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,449 @@ +/* +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 +*/ + +#include "igmp.h" + +#include "ipv4addressdelegate.h" +#include "iputils.h" + +#include +#include + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* IgmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new IgmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IgmpProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kRsvdMrtCode, FieldValue).toString()); +} + +void IgmpProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..6ee9ad3 --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,111 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "igmp.pb.h" +#include "gmp.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..a7d6ef7 --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,752 @@ +/* +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 +*/ + +#include +#include + +#include "ip4.h" + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Ip4Protocol::~Ip4Protocol() +{ + delete configForm; +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +int Ip4Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.src_ip_count()); + + if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.dst_ip_count()); + + return count; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip4Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + + configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); + + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + configWidget(); + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + + data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..6f89b09 --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,116 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..266c8c2 --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,953 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +int Ip6Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + 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) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..dfaf02d --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,131 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..5adecf1 --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,331 @@ +/* +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 +*/ + +#include +#include + +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +LlcProtocol::~LlcProtocol() +{ + delete configForm; +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + + +QWidget* LlcProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configWidget(); + + configForm->cbOverrideDsap->setChecked( + fieldData(llc_is_override_dsap, FieldValue).toBool()); + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideSsap->setChecked( + fieldData(llc_is_override_ssap, FieldValue).toBool()); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideControl->setChecked( + fieldData(llc_is_override_ctl, FieldValue).toBool()); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(llc_is_override_dsap, + configForm->cbOverrideDsap->isChecked()); + setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ssap, + configForm->cbOverrideSsap->isChecked()); + setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ctl, + configForm->cbOverrideControl->isChecked()); + setFieldData(llc_ctl, + configForm->leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..808d442 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..788a10d --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,329 @@ +/* +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 +*/ + +#include +#include + +#include "mac.h" + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +MacProtocol::~MacProtocol() +{ + delete configForm; +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + +int MacProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.dst_mac_count()); + + if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.src_mac_count()); + + return count; +} + +QWidget* MacProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..32cd7e2 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..4c373ae --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,624 @@ +/* +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 +*/ + +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" +#include "iputils.h" + +#include +#include + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j+8)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* MldProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new MldConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void MldProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kMldMrt, FieldValue).toString()); +} + +void MldProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kMldMrt, configForm->maxResponseTime->text()); +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..bc2d95e --- /dev/null +++ b/common/mld.h @@ -0,0 +1,108 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "mld.pb.h" +#include "gmp.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..f0563fc --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,142 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network script xml +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf +FORMS += \ + pcapfileimport.ui \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + hexdump.ui \ + sample.ui +PROTOS += \ + protocol.proto \ + fileformat.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto +HEADERS += \ + ostprotolib.h \ + abstractprotocol.h \ + comboprotocol.h \ + abstractfileformat.h \ + fileformat.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + arp.h \ + ip4.h \ + ip6.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + ip6over4.h \ + ip4over6.h \ + ip4over4.h \ + ip6over6.h \ + icmp.h \ + gmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + userscript.h \ + hexdump.h \ + sample.h +SOURCES += \ + ostprotolib.cpp \ + abstractprotocol.cpp \ + crc32c.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + icmp.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + userscript.cpp \ + hexdump.cpp \ + sample.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..e46dc8c --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} + diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..7fd0dd5 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,284 @@ +/* +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 +*/ + +#include +#include + +//#include "../client/stream.h" +#include "payload.h" +#include "streambase.h" + + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +PayloadProtocol::~PayloadProtocol() +{ + delete configForm; +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + +int PayloadProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.pattern_mode() == OstProto::Payload::e_dp_random) + { + switch(mpStream->sendUnit()) + { + case OstProto::StreamControl::e_su_packets: + return mpStream->numPackets(); + + case OstProto::StreamControl::e_su_bursts: + return int(mpStream->numBursts() + * mpStream->burstSize() + * mpStream->burstRate()); + } + } + + if (mpStream->lenMode() != StreamBase::e_fl_fixed) + { + count = AbstractProtocol::lcm(count, + mpStream->frameLenMax() - mpStream->frameLenMin() + 1); + } + + return count; +} + +QWidget* PayloadProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void PayloadProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..822ee37 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,85 @@ +/* +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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..bc0ccd6 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,661 @@ +/* +Copyright (C) 2011 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 "pcapfileformat.h" + +#include "pdmlreader.h" +#include "ostprotolib.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; +const quint32 kDltEthernet = 1; + +PcapFileFormat pcapFileFormat; + +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + +PcapFileFormat::PcapFileFormat() +{ + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + + importDialog_ = NULL; +} + +PcapFileFormat::~PcapFileFormat() +{ + delete importDialog_; +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QTemporaryFile file2; + quint32 magic; + uchar gzipMagic[2]; + int len; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; + + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; + + emit status("Decompressing..."); + emit target(0); + + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + gzip.start(OstProtoLib::gzipPath(), + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + + fd_ >> magic; + + qDebug("magic = %08x", magic); + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); + else + fd_.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif + + pktBuf.resize(fileHdr.snapLen); + + if (importOptions_.value("ViaPdml").toBool()) + { + QProcess tshark; + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + diff.start(OstProtoLib::diffPath(), + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff. Check path in Preferences.\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + + goto _exit; + } + +_non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage + pktCount = 1; + while (!fd_.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + readPacket(pktHdr, pktBuf); + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; + pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; + } + + isOk = true; + goto _exit; + +_user_cancel: + isOk = true; + goto _exit; + +_diff_fail: + goto _exit; + +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +#if 0 +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + file.close(); + return isOk; +} + +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd_.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = kDltEthernet; + + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + emit status("Writing Packets..."); + emit target(streams.stream_size()); + + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += 1000000/s.packetRate(); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } + + emit progress(i); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..064aaf1 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "abstractfileformat.h" +#include "ui_pcapfileimport.h" + +#include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; + +class PdmlReader; +class PcapFileFormat : public AbstractFileFormat +{ + friend class PdmlReader; + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + virtual QDialog* openOptionsDialog(); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..9ba1f2d --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,163 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" + +#include "ostprotolib.h" +#include "pdmlreader.h" + +#include +#include + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } + + goto _exit; + +_open_fail: + isOk = false; + +_user_cancel: +_exit: + delete reader; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; +} + +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + xml.readNext(); + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..e05026a --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "abstractfileformat.h" + +class PdmlFileFormat : public AbstractFileFormat +{ +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..cb39a4f --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,153 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +const int kBaseHex = 16; + +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +PdmlProtocol::~PdmlProtocol() +{ +} + +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..412f588 --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,64 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +class PdmlProtocol +{ +public: + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlProtocol(); + + int ostProtoId_; + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp new file mode 100644 index 0000000..31e1303 --- /dev/null +++ b/common/pdmlprotocols.cpp @@ -0,0 +1,1357 @@ +/* +Copyright (C) 2011 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 "pdmlprotocols.h" + +#include "arp.pb.h" +#include "eth2.pb.h" +#include "dot3.pb.h" +#include "gmp.pb.h" +#include "hexdump.pb.h" +#include "llc.pb.h" +#include "mac.pb.h" +#include "icmp.pb.h" +#include "igmp.pb.h" +#include "ip4.pb.h" +#include "ip6.pb.h" +#include "mld.pb.h" +#include "sample.pb.h" +#include "snap.pb.h" +#include "svlan.pb.h" +#include "tcp.pb.h" +#include "textproto.pb.h" +#include "udp.pb.h" +#include "vlan.pb.h" + +#include +#include + +const int kBaseHex = 16; + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +PdmlProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + endPos_ = pos; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + // Skipped field(s) at end? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), + pos, expPos_, endPos_); + + // Skipped field? Pad with zero! + if ((pos > expPos_) && (expPos_ < endPos_)) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ +} + +PdmlProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ +} + +PdmlProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} + + +// ---------------------------------------------------------- // +// PdmlSvlanProtocol // +// ---------------------------------------------------------- // + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlVlanProtocol // +// ---------------------------------------------------------- // + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlEthProtocol // +// ---------------------------------------------------------- // + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + + +// ---------------------------------------------------------- // +// PdmlLlcProtocol // +// ---------------------------------------------------------- // + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + + +// ---------------------------------------------------------- // +// PdmlArpProtocol // +// ---------------------------------------------------------- // + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + + +// ---------------------------------------------------------- // +// PdmlIp4Protocol // +// ---------------------------------------------------------- // + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlIp6Protocol // +// ---------------------------------------------------------- // + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + + +// ---------------------------------------------------------- // +// PdmlIcmpProtocol // +// ---------------------------------------------------------- // + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + +// ---------------------------------------------------------- // +// PdmlIcmp6Protocol // +// ---------------------------------------------------------- // + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + + +// ---------------------------------------------------------- // +// PdmlIgmpProtocol // +// ---------------------------------------------------------- // + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + + +// ---------------------------------------------------------- // +// PdmlMldProtocol // +// ---------------------------------------------------------- // + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + + +// ---------------------------------------------------------- // +// PdmlTcpProtocol // +// ---------------------------------------------------------- // + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlUdpProtocol // +// ---------------------------------------------------------- // + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + + +// ---------------------------------------------------------- // +// PdmlTextProtocol // +// ---------------------------------------------------------- // + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h new file mode 100644 index 0000000..95a75c0 --- /dev/null +++ b/common/pdmlprotocols.h @@ -0,0 +1,313 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H + +#include "pdmlprotocol.h" + +class PdmlUnknownProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlGenInfoProtocol(); + +}; + +class PdmlFrameProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); +}; + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..c4ea3f0 --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,533 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdmlprotocols.h" + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + QStringList creator; + + Q_ASSERT(isStartElement() && name() == "pdml"); + + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..7de3918 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + bool isMldSupport_; + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..9a74654 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,249 @@ +/* +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 +*/ + +package OstProto; +option cc_generic_services = true; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; + optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; + optional double packets_per_sec = 9 [default = 1]; + optional double bursts_per_sec = 10 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +enum TransmitMode { + kSequentialTransmit = 0; + kInterleavedTransmit = 1; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; + optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; + + optional uint64 rx_drops = 100; + optional uint64 rx_errors = 101; + optional uint64 rx_fifo_errors = 102; + optional uint64 rx_frame_errors = 103; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..e91f270 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,212 @@ +/* +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 +*/ + +#include "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" +#include "vlan.h" +#include "vlanstack.h" +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" +#include "ip4over4.h" +#include "ip6over6.h" +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" +#include "textproto.h" +#include "userscript.h" +#include "hexdump.h" +#include "sample.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..07b0604 --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,57 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..42f9f32 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,546 @@ +/* +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 +*/ + +#include + +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +SampleProtocol::~SampleProtocol() +{ + delete configForm; +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame has any variable fields or has a variable + size, return the minimum number of frames required to vary the fields \n + + Otherwise you don't need to reimplement this method - the base class always + returns 1 +*/ +int SampleProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +QWidget* SampleProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ +void SampleProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ +void SampleProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..90b9be8 --- /dev/null +++ b/common/sample.h @@ -0,0 +1,103 @@ +/* +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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "sample.pb.h" +#include "ui_sample.h" + +#include "abstractprotocol.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + +public: + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..bdb80c3 --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,307 @@ +/* +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 +*/ + +#include +#include + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +SnapProtocol::~SnapProtocol() +{ + delete configForm; +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} + +QWidget* SnapProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideOui->setChecked( + fieldData(snap_is_override_oui, FieldValue).toBool()); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + + configForm->cbOverrideType->setChecked( + fieldData(snap_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + configWidget(); + + setFieldData(snap_is_override_oui, + configForm->cbOverrideOui->isChecked()); + setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + + setFieldData(snap_is_override_type, + configForm->cbOverrideType->isChecked()); + setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..daba802 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + +public: + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..ab43117 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,565 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +/*! Convenience Function */ +quint16 StreamBase::frameLenAvg() const +{ + quint16 avgFrameLen; + + if (lenMode() == e_fl_fixed) + avgFrameLen = frameLen(); + else + avgFrameLen = (frameLenMin() + frameLenMax())/2; + + return avgFrameLen; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +double StreamBase::packetRate() const +{ + return (double) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(double packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +double StreamBase::burstRate() const +{ + return (double) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(double burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +/*! Convenience Function */ +double StreamBase::averagePacketRate() const +{ + double avgPacketRate; + + switch (sendUnit()) + { + case e_su_bursts: + avgPacketRate = burstRate() * burstSize(); + break; + case e_su_packets: + avgPacketRate = packetRate(); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return avgPacketRate; +} + +/*! Convenience Function */ +bool StreamBase::setAveragePacketRate(double packetsPerSec) +{ + switch (sendUnit()) + { + case e_su_bursts: + setBurstRate(packetsPerSec/double(burstSize())); + break; + case e_su_packets: + setPacketRate(packetsPerSec); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +int StreamBase::frameVariableCount() const +{ + ProtocolListIterator *iter; + quint64 frameCount = 1; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + int count; + + proto = iter->next(); + count = proto->protocolFrameVariableCount(); + + // correct count for mis-behaving protocols + if (count <= 0) + count = 1; + + frameCount = AbstractProtocol::lcm(frameCount, count); + } + delete iter; + + return frameCount; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..9ef3ba1 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + quint16 frameLenAvg() const; + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + double packetRate() const; + bool setPacketRate(double packetsPerSec); + + double burstRate() const; + bool setBurstRate(double burstsPerSec); + + double averagePacketRate() const; + bool setAveragePacketRate(double packetsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameVariableCount() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..893671d --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,68 @@ +/* +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 +*/ + +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..39c71bf --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,709 @@ +/* +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 +*/ + +#include +#include + +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +TcpProtocol::~TcpProtocol() +{ + delete configForm; +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int TcpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + +QWidget* TcpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); + + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); + + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); + + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); + + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); + + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + bool isOk; + int ff = 0; + + configWidget(); + + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); + + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); + + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); + + setFieldData(tcp_window, configForm->leTcpWindow->text()); + + setFieldData(tcp_cksum, configForm->leTcpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); + + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + setFieldData(tcp_flags, ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..9addef0 --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,101 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..9834530 --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,295 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->eolCombo->setCurrentIndex( + fieldData(textProto_eol, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..1ec5fc0 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..f6996aa --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProtocol + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + +
    diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..5a4e14b --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,500 @@ +/* +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 +*/ + +#include +#include + +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +UdpProtocol::~UdpProtocol() +{ + delete configForm; +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int UdpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + +QWidget* UdpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); + + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); + + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); + + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); + + setFieldData(udp_cksum, configForm->leUdpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..7bdf200 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,88 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..fece963 --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,630 @@ +/* +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 +*/ + +#include "userscript.h" + +#include + +// +// -------------------- UserScriptConfigForm -------------------- +// + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), protocol_(protocol) +{ + setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } + updateStatus(); +} + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + configForm = NULL; + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +int UserScriptProtocol::protocolFrameVariableCount() const +{ + return userProtocol_.protocolFrameVariableCount(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; + protocolFrameVariableCount_ = 1; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +int UserProtocol::protocolFrameVariableCount() const +{ + return protocolFrameVariableCount_; +} + +void UserProtocol::setProtocolFrameVariableCount(int count) +{ + protocolFrameVariableCount_ = count; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +int UserProtocol::protocolFramePayloadVariableCount() const +{ + return parent_->protocolFramePayloadVariableCount(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..99ac226 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,190 @@ +/* +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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + Q_PROPERTY(int protocolFrameVariableCount + READ protocolFrameVariableCount + WRITE setProtocolFrameVariableCount); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp, + ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + int protocolFrameVariableCount() const; + void setProtocolFrameVariableCount(int count); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; + int protocolFrameVariableCount_; +}; + + + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + void updateStatus(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + + +class UserScriptProtocol : public AbstractProtocol +{ + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..95b2304 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,257 @@ +/* +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 +*/ + +#include + +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +VlanProtocol::~VlanProtocol() +{ + delete configForm; +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..728572b --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +protected: + OstProto::Vlan data; + +public: + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..3e0326d --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,179 @@ + + Vlan + + + + 0 + 0 + 268 + 96 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..89479e7 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/commands.h\ + src/qhexedit.h \ + src/qhexedit_p.h \ + src/xbytearray.h + +SOURCES = src/commands.cpp \ + src/qhexedit.cpp \ + src/qhexedit_p.cpp \ + src/xbytearray.cpp diff --git a/extra/qhexedit2/src/commands.cpp b/extra/qhexedit2/src/commands.cpp new file mode 100644 index 0000000..303091d --- /dev/null +++ b/extra/qhexedit2/src/commands.cpp @@ -0,0 +1,115 @@ +#include "commands.h" + +CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _xData = xData; + _charPos = charPos; + _newChar = newChar; + _cmd = cmd; +} + +bool CharCommand::mergeWith(const QUndoCommand *command) +{ + const CharCommand *nextCommand = static_cast(command); + bool result = false; + + if (_cmd != remove) + { + if (nextCommand->_cmd == replace) + if (nextCommand->_charPos == _charPos) + { + _newChar = nextCommand->_newChar; + result = true; + } + } + return result; +} + +void CharCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_charPos, 1); + break; + case replace: + _xData->replace(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + case remove: + _xData->insert(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + } +} + +void CharCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_charPos, _newChar); + break; + case replace: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->replace(_charPos, _newChar); + break; + case remove: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->remove(_charPos, 1); + break; + } +} + + + +ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _cmd = cmd; + _xData = xData; + _baPos = baPos; + _newBa = newBa; + _len = len; +} + +void ArrayCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_baPos, _newBa.length()); + break; + case replace: + _xData->replace(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + case remove: + _xData->insert(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + } +} + +void ArrayCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_baPos, _newBa); + break; + case replace: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->replace(_baPos, _newBa); + break; + case remove: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->remove(_baPos, _len); + break; + } +} diff --git a/extra/qhexedit2/src/commands.h b/extra/qhexedit2/src/commands.h new file mode 100644 index 0000000..9931b3f --- /dev/null +++ b/extra/qhexedit2/src/commands.h @@ -0,0 +1,70 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +/** \cond docNever */ + +#include + +#include "xbytearray.h" + +/*! CharCommand is a class to prived undo/redo functionality in QHexEdit. +A QUndoCommand represents a single editing action on a document. CharCommand +is responsable for manipulations on single chars. It can insert. replace and +remove characters. A manipulation stores allways to actions +1. redo (or do) action +2. undo action. + +CharCommand also supports command compression via mergeWidht(). This allows +the user to execute a undo command contation e.g. 3 steps in a single command. +If you for example insert a new byt "34" this means for the editor doing 3 +steps: insert a "00", replace it with "03" and the replace it with "34". These +3 steps are combined into a single step, insert a "34". +*/ +class CharCommand : public QUndoCommand +{ +public: + enum { Id = 1234 }; + enum Cmd {insert, remove, replace}; + + CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, + QUndoCommand *parent=0); + + void undo(); + void redo(); + bool mergeWith(const QUndoCommand *command); + int id() const { return Id; } + +private: + XByteArray * _xData; + int _charPos; + bool _wasChanged; + char _newChar; + char _oldChar; + Cmd _cmd; +}; + +/*! ArrayCommand provides undo/redo functionality for handling binary strings. It +can undo/redo insert, replace and remove binary strins (QByteArrays). +*/ +class ArrayCommand : public QUndoCommand +{ +public: + enum Cmd {insert, remove, replace}; + ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, + QUndoCommand *parent=0); + void undo(); + void redo(); + +private: + Cmd _cmd; + XByteArray * _xData; + int _baPos; + int _len; + QByteArray _wasChanged; + QByteArray _newBa; + QByteArray _oldBa; +}; + +/** \endcond docNever */ + +#endif // COMMANDS_H diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..b6dd38d --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,152 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); + connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); + setFocusPolicy(Qt::NoFocus); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +QString QHexEdit::toReadableString() +{ + return qHexEdit_p->toRedableString(); +} + +QString QHexEdit::selectionToReadableString() +{ + return qHexEdit_p->selectionToReadableString(); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::redo() +{ + qHexEdit_p->redo(); +} + +void QHexEdit::undo() +{ + qHexEdit_p->undo(); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return qHexEdit_p->addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setSelectionColor(const QColor &color) +{ + qHexEdit_p->setSelectionColor(color); +} + +QColor QHexEdit::selectionColor() +{ + return qHexEdit_p->selectionColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setReadOnly(bool readOnly) +{ + qHexEdit_p->setReadOnly(readOnly); +} + +bool QHexEdit::isReadOnly() +{ + return qHexEdit_p->isReadOnly(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + +const QFont & QHexEdit::font() const +{ + return qHexEdit_p->font(); +} diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..b5a9601 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,205 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.6.1 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text +data. There are sip configuration files included, so it is easy to create +bindings for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use +the mouse or the keyboard to navigate inside the widget. If you hit the keys +(0..9, a..f) you will change the data. Changed data is highlighted and can be +accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) +and insert data. In this case the size of data() increases. It is also possible +to delete bytes (del or backspace), here the size of data decreases. + +You can select data with keyboard hits or mouse movements. The copy-key will +copy the selected data into the clipboard. The cut-key copies also but delets +it afterwards. In overwrite mode, the paste function overwrites the content of +the (does not change the length) data. In insert mode, clipboard data will be +inserted. The clipboard content is expected in ASCII Hex notation. Unknown +characters will be ignored. + +QHexEdit comes with undo/redo functionality. All changes can be undone, by +pressing the undo-key (usually ctr-z). They can also be redone afterwards. +The undo/redo framework is cleared, when setData() sets up a new +content for the editor. + +This widget can only handle small amounts of data. The size has to be below 10 +megabytes, otherwise the scroll sliders ard not shown and you can't scroll any +more. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Property selection color sets (setSelectionColor()) the backgorund + color of selected text areas. You can also read the color + (selectionColor()). + */ + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwrite mode the user will overwrite existing data. The + size of data will be constant. In insert mode the size will grow, when inserting + new data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + + /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode + in which the editor works. In readonly mode the the user can only navigate + through the data and select data; modifying is not possible. This + property's default is false. + */ + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + Q_PROPERTY(QFont font READ font WRITE setFont) + + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + In overwrite mode, the existing bytes will be overwriten with 0x00. + */ + void remove(int pos, int len=1); + + /*! Gives back a formatted image of the content of QHexEdit + */ + QString toReadableString(); + + /*! Gives back a formatted image of the selected content of QHexEdit + */ + QString selectionToReadableString(); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setSelectionColor(QColor const &color); + QColor selectionColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + void setReadOnly(bool); + bool isReadOnly(); + const QFont &font() const; + void setFont(const QFont &); + /*! \endcond docNever */ + +public slots: + /*! Redoes the last operation. If there is no operation to redo, i.e. + there is no redo step in the undo/redo history, nothing happens. + */ + void redo(); + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + + /*! Undoes the last operation. If there is no operation to undo, i.e. + there is no undo step in the undo/redo history, nothing happens. + */ + void undo(); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddressChanged(int address); + + /*! Contains the size of the data to edit. */ + void currentSizeChanged(int size); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..2f046bb --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,800 @@ +#include + +#include "qhexedit_p.h" +#include "commands.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _undoStack = new QUndoStack(this); + + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setReadOnly(false); + setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); + setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); + setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); + setFont(QFont("Courier", 10)); + + _size = 0; + resetSelection(0); + + setFocusPolicy(Qt::StrongFocus); + + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + _cursorTimer.setInterval(500); + _cursorTimer.start(); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _xData.setAddressOffset(offset); + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _xData.addressOffset(); +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _xData.setData(data); + _undoStack->clear(); + adjust(); + setCursorPos(0); +} + +QByteArray QHexEditPrivate::data() +{ + return _xData.data(); +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setSelectionColor(const QColor &color) +{ + _selectionColor = color; + update(); +} + +QColor QHexEditPrivate::selectionColor() +{ + return _selectionColor; +} + +void QHexEditPrivate::setReadOnly(bool readOnly) +{ + _readOnly = readOnly; +} + +bool QHexEditPrivate::isReadOnly() +{ + return _readOnly; +} + +XByteArray & QHexEditPrivate::xData() +{ + return _xData; +} + +void QHexEditPrivate::insert(int index, const QByteArray & ba) +{ + if (ba.length() > 0) + { + if (_overwriteMode) + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } +} + +void QHexEditPrivate::insert(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::remove(int index, int len) +{ + if (len > 0) + { + if (len == 1) + { + if (_overwriteMode) + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + else + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + } + else + { + QByteArray ba = QByteArray(len, char(0)); + if (_overwriteMode) + { + QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } + } +} + +void QHexEditPrivate::replace(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::replace(int index, const QByteArray & ba) +{ + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + _xData.setAddressWidth(addressWidth); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + _overwriteMode = overwriteMode; +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::redo() +{ + _undoStack->redo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +void QHexEditPrivate::undo() +{ + _undoStack->undo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +QString QHexEditPrivate::toRedableString() +{ + return _xData.toRedableString(); +} + + +QString QHexEditPrivate::selectionToReadableString() +{ + return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + +/*****************************************************************************/ +/* Cursor movements */ +/*****************************************************************************/ + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + { + setCursorPos(_cursorPosition - 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfLine)) + { + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfLine)) + { + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousLine)) + { + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousPage)) + { + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_xData.size() * 2); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfDocument)) + { + setCursorPos(0); + resetSelection(_cursorPosition); + } + +/*****************************************************************************/ +/* Select commands */ +/*****************************************************************************/ + if (event->matches(QKeySequence::SelectAll)) + { + resetSelection(0); + setSelection(2*_xData.size() + 1); + } + if (event->matches(QKeySequence::SelectNextChar)) + { + int pos = _cursorPosition + 1; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousChar)) + { + int pos = _cursorPosition - 1; + setSelection(pos); + setCursorPos(pos); + } + if (event->matches(QKeySequence::SelectEndOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousLine)) + { + int pos = _cursorPosition - (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectNextLine)) + { + int pos = _cursorPosition + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + + if (event->matches(QKeySequence::SelectNextPage)) + { + int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousPage)) + { + int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectEndOfDocument)) + { + int pos = _xData.size() * 2; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfDocument)) + { + int pos = 0; + setCursorPos(pos); + setSelection(pos); + } + +/*****************************************************************************/ +/* Edit Commands */ +/*****************************************************************************/ +if (!_readOnly) +{ + /* Hex input */ + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + + // If insert mode, then insert a byte + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + } + + // Change content + if (_xData.size() > 0) + { + QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + + replace(posBa, QByteArray().fromHex(hexValue)[0]); + + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + } + + /* Cut & Paste */ + if (event->matches(QKeySequence::Cut)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append("\n"); + } + remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + setCursorPos(getSelectionBegin()); + resetSelection(getSelectionBegin()); + } + + if (event->matches(QKeySequence::Paste)) + { + QClipboard *clipboard = QApplication::clipboard(); + QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); + insert(_cursorPosition / 2, ba); + setCursorPos(_cursorPosition + 2 * ba.length()); + resetSelection(getSelectionBegin()); + } + + + /* Delete char */ + if (event->matches(QKeySequence::Delete)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (_overwriteMode) + replace(posBa, char(0)); + else + remove(posBa, 1); + } + } + + /* Backspace */ + if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (posBa > 0) + { + if (_overwriteMode) + replace(posBa - 1, char(0)); + else + remove(posBa - 1, 1); + setCursorPos(_cursorPosition - 2); + } + } + } + + /* undo */ + if (event->matches(QKeySequence::Undo)) + { + undo(); + } + + /* redo */ + if (event->matches(QKeySequence::Redo)) + { + redo(); + } + + } + + if (event->matches(QKeySequence::Copy)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append('\n'); + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + } + + // Switch between insert/overwrite mode + if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) + { + _overwriteMode = !_overwriteMode; + setCursorPos(_cursorPosition); + overwriteModeChanged(_overwriteMode); + } + + _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); + update(); +} + +void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int actPos = cursorPos(event->pos()); + setCursorPos(actPos); + setSelection(actPos); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int cPos = cursorPos(event->pos()); + resetSelection(cPos); + setCursorPos(cPos); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _xData.size()) + lastLineIdx = _xData.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); + QBrush selected = QBrush(_selectionColor); + QPen colSelected = QPen(Qt::white); + QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); + + painter.setBackgroundMode(Qt::TransparentMode); + + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + int posBa = lineIdx + colIdx; + if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) + { + painter.setBackground(selected); + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setPen(colSelected); + } + else + { + if (_highlighting) + { + // hilight diff bytes + painter.setBackground(highLighted); + if (_xData.dataChanged(posBa)) + { + painter.setPen(colHighlighted); + painter.setBackgroundMode(Qt::OpaqueMode); + } + else + { + painter.setPen(colStandard); + painter.setBackgroundMode(Qt::TransparentMode); + } + } + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + + } + } + painter.setBackgroundMode(Qt::TransparentMode); + painter.setPen(this->palette().color(QPalette::WindowText)); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + int xPosAscii = _xPosAscii; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); + xPosAscii += _charWidth; + } + } + } + + // paint cursor + if (_blink) + { + if (_overwriteMode) + painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); + else + painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); + } + + if (_size != _xData.size()) + { + _size = _xData.size(); + emit currentSizeChanged(_size); + } +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_xData.size() * 2 - 1)) + position = _xData.size() * 2 - 1; + } else { + if (position > (_xData.size() * 2)) + position = _xData.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddressChanged(_cursorPosition/2); +} + +int QHexEditPrivate::cursorPos(QPoint pos) +{ + int result = -1; + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; + result = x + y; + } + return result; +} + +int QHexEditPrivate::cursorPos() +{ + return _cursorPosition; +} + +void QHexEditPrivate::resetSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + _selectionInit = pos; + _selectionBegin = pos; + _selectionEnd = pos; +} + +void QHexEditPrivate::setSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + if (pos >= _selectionInit) + { + _selectionEnd = pos; + _selectionBegin = _selectionInit; + } + else + { + _selectionBegin = pos; + _selectionEnd = _selectionInit; + } +} + +int QHexEditPrivate::getSelectionBegin() +{ + return _selectionBegin; +} + +int QHexEditPrivate::getSelectionEnd() +{ + return _selectionEnd; +} + + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..b802af3 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,120 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include +#include "xbytearray.h" + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setAddressOffset(int offset); + int addressOffset(); + + void setCursorPos(int position); + int cursorPos(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void setReadOnly(bool readOnly); + bool isReadOnly(); + + void setSelectionColor(QColor const &color); + QColor selectionColor(); + + XByteArray & xData(); + + void insert(int index, const QByteArray & ba); + void insert(int index, char ch); + void remove(int index, int len=1); + void replace(int index, char ch); + void replace(int index, const QByteArray & ba); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + + void undo(); + void redo(); + + QString toRedableString(); + QString selectionToReadableString(); + +signals: + void currentAddressChanged(int address); + void currentSizeChanged(int size); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mousePressEvent(QMouseEvent * event); + + void paintEvent(QPaintEvent *event); + + int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION + + void resetSelection(int pos); + void setSelection(int pos); // set min (if below init) or max (if greater init) + int getSelectionBegin(); + int getSelectionEnd(); + + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QColor _highlightingColor; + QColor _selectionColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + QUndoStack *_undoStack; + + XByteArray _xData; // Hält den Inhalt des Hex Editors + + bool _blink; // true: then cursor blinks + bool _renderingRequired; // Flag to store that rendering is necessary + bool _addressArea; // left area of QHexEdit + bool _asciiArea; // medium area + bool _highlighting; // highlighting of changed bytes + bool _overwriteMode; + bool _readOnly; // true: the user can only look and navigate + + int _charWidth, _charHeight; // char dimensions (dpendend on font) + int _cursorX, _cursorY; // graphics position of the cursor + int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) + int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas + + int _selectionBegin; // First selected char + int _selectionEnd; // Last selected char + int _selectionInit; // That's, where we pressed the mouse button + + int _size; +}; + +/** \endcond docNever */ + +#endif + diff --git a/extra/qhexedit2/src/xbytearray.cpp b/extra/qhexedit2/src/xbytearray.cpp new file mode 100644 index 0000000..ec8bf3d --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.cpp @@ -0,0 +1,167 @@ +#include "xbytearray.h" + +XByteArray::XByteArray() +{ + _oldSize = -99; + _addressNumbers = 4; + _addressOffset = 0; + +} + +int XByteArray::addressOffset() +{ + return _addressOffset; +} + +void XByteArray::setAddressOffset(int offset) +{ + _addressOffset = offset; +} + +int XByteArray::addressWidth() +{ + return _addressNumbers; +} + +void XByteArray::setAddressWidth(int width) +{ + if ((width >= 0) and (width<=6)) + { + _addressNumbers = width; + } +} + +QByteArray & XByteArray::data() +{ + return _data; +} + +void XByteArray::setData(QByteArray data) +{ + _data = data; + _changedData = QByteArray(data.length(), char(0)); +} + +bool XByteArray::dataChanged(int i) +{ + return bool(_changedData[i]); +} + +QByteArray XByteArray::dataChanged(int i, int len) +{ + return _changedData.mid(i, len); +} + +void XByteArray::setDataChanged(int i, bool state) +{ + _changedData[i] = char(state); +} + +void XByteArray::setDataChanged(int i, const QByteArray & state) +{ + int length = state.length(); + int len; + if ((i + length) > _changedData.length()) + len = _changedData.length() - i; + else + len = length; + _changedData.replace(i, len, state); +} + +int XByteArray::realAddressNumbers() +{ + if (_oldSize != _data.size()) + { + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + } + return _realAddressNumbers; +} + +int XByteArray::size() +{ + return _data.size(); +} + +QByteArray & XByteArray::insert(int i, char ch) +{ + _data.insert(i, ch); + _changedData.insert(i, char(1)); + return _data; +} + +QByteArray & XByteArray::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _changedData.insert(i, QByteArray(ba.length(), char(1))); + return _data; +} + +QByteArray & XByteArray::remove(int i, int len) +{ + _data.remove(i, len); + _changedData.remove(i, len); + return _data; +} + +QByteArray & XByteArray::replace(int index, char ch) +{ + _data[index] = ch; + _changedData[index] = char(1); + return _data; +} + +QByteArray & XByteArray::replace(int index, const QByteArray & ba) +{ + int len = ba.length(); + return replace(index, len, ba); +} + +QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) +{ + int len; + if ((index + length) > _data.length()) + len = _data.length() - index; + else + len = length; + _data.replace(index, len, ba.mid(0, len)); + _changedData.replace(index, len, QByteArray(len, char(1))); + return _data; +} + +QChar XByteArray::asciiChar(int index) +{ + char ch = _data[index]; + if ((ch < 0x20) or (ch > 0x7e)) + ch = '.'; + return QChar(ch); +} + +QString XByteArray::toRedableString(int start, int end) +{ + int adrWidth = realAddressNumbers(); + if (_addressNumbers > adrWidth) + adrWidth = _addressNumbers; + if (end < 0) + end = _data.size(); + + QString result; + for (int i=start; i < end; i += 16) + { + QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); + QString hexStr; + QString ascStr; + for (int j=0; j<16; j++) + { + if ((i + j) < _data.size()) + { + hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); + ascStr.append(asciiChar(i+j)); + } + } + result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; + } + return result; +} diff --git a/extra/qhexedit2/src/xbytearray.h b/extra/qhexedit2/src/xbytearray.h new file mode 100644 index 0000000..2b67c61 --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.h @@ -0,0 +1,66 @@ +#ifndef XBYTEARRAY_H +#define XBYTEARRAY_H + +/** \cond docNever */ + +#include + +/*! XByteArray represents the content of QHexEcit. +XByteArray comprehend the data itself and informations to store if it was +changed. The QHexEdit component uses these informations to perform nice +rendering of the data + +XByteArray also provides some functionality to insert, replace and remove +single chars and QByteArras. Additionally some functions support rendering +and converting to readable strings. +*/ +class XByteArray +{ +public: + explicit XByteArray(); + + int addressOffset(); + void setAddressOffset(int offset); + + int addressWidth(); + void setAddressWidth(int width); + + QByteArray & data(); + void setData(QByteArray data); + + bool dataChanged(int i); + QByteArray dataChanged(int i, int len); + void setDataChanged(int i, bool state); + void setDataChanged(int i, const QByteArray & state); + + int realAddressNumbers(); + int size(); + + QByteArray & insert(int i, char ch); + QByteArray & insert(int i, const QByteArray & ba); + + QByteArray & remove(int pos, int len); + + QByteArray & replace(int index, char ch); + QByteArray & replace(int index, const QByteArray & ba); + QByteArray & replace(int index, int length, const QByteArray & ba); + + QChar asciiChar(int index); + QString toRedableString(int start=0, int end=-1); + +signals: + +public slots: + +private: + QByteArray _data; + QByteArray _changedData; + + int _addressNumbers; // wanted width of address area + int _addressOffset; // will be added to the real addres inside bytearray + int _realAddressNumbers; // real width of address area (can be greater then wanted width) + int _oldSize; // size of data +}; + +/** \endcond docNever */ +#endif // XBYTEARRAY_H diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..0f9d987 --- /dev/null +++ b/ost.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..f53fa81 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..7c7789e --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[PB_HDR_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..e1fbdf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,39 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..2d1bd63 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,291 @@ +/* +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 +*/ + +//#include "pbhelper.h" +#include "rpcserver.h" +#include "pbqtio.h" + +#include + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcServer::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + +_exit: + delete controller; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + delete inStream; + delete outStream; + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcServer::when_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit2; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); + + parsing = false; + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..76f179a --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..4aa97c6 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,594 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#define __STDC_FORMAT_MACROS + +#include "abstractport.h" + +#include "../common/streambase.h" +#include "../common/abstractprotocol.h" + +#include +#include + +#include +#include +#include + +AbstractPort::AbstractPort(int id, const char *device) +{ + isUsable_ = true; + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + minPacketSetSize_ = 1; + + maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + if (port.has_transmit_mode()) + data_.set_transmit_mode(port.transmit_mode()); + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::addNote(QString note) +{ + QString notes = QString::fromStdString(data_.notes()); + + note.prepend("
  • "); + note.append("
  • "); + + if (notes.isEmpty()) + notes="Limitation(s)
      "; + else + notes.remove("
    "); + + notes.append(note); + notes.append(""); + + data_.set_notes(notes.toStdString()); +} + +void AbstractPort::updatePacketList() +{ + switch(data_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + updatePacketListSequential(); + break; + case OstProto::kInterleavedTransmit: + updatePacketListInterleaved(); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } +} + +void AbstractPort::updatePacketListSequential() +{ + long sec = 0; + long nsec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + int len; + ulong n, x, y; + ulong burstSize; + double ibg = 0; + quint64 ibg1 = 0, ibg2 = 0; + quint64 nb1 = 0, nb2 = 0; + double ipg = 0; + quint64 ipg1 = 0, ipg2 = 0; + quint64 npx1 = 0, npx2 = 0; + quint64 npy1 = 0, npy2 = 0; + quint64 loopDelay; + ulong frameVariableCount = streamList_[i]->frameVariableCount(); + + // We derive n, x, y such that + // n * x + y = total number of packets to be sent + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + burstSize = streamList_[i]->burstSize(); + x = AbstractProtocol::lcm(frameVariableCount, burstSize); + n = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) / x; + y = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) % x; + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + ibg1 = quint64(ceil(ibg)); + ibg2 = quint64(floor(ibg)); + nb1 = quint64((ibg - double(ibg2)) * double(x)); + nb2 = x - nb1; + } + loopDelay = ibg2; + break; + case OstProto::StreamControl::e_su_packets: + x = frameVariableCount; + n = 2; + while (x < minPacketSetSize_) + x = frameVariableCount*n++; + n = streamList_[i]->numPackets() / x; + y = streamList_[i]->numPackets() % x; + burstSize = x + y; + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + ipg1 = quint64(ceil(ipg)); + ipg2 = quint64(floor(ipg)); + npx1 = quint64((ipg - double(ipg2)) * double(x)); + npx2 = x - npx1; + npy1 = quint64((ipg - double(ipg2)) * double(y)); + npy2 = y - npy1; + } + loopDelay = ipg2; + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + + qDebug("\nframeVariableCount = %lu", frameVariableCount); + qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", + n, x, y, burstSize); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, ibg1); + qDebug("nb1 = %" PRIu64, nb1); + qDebug("ibg2 = %" PRIu64, ibg2); + qDebug("nb2 = %" PRIu64 "\n", nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, ipg1); + qDebug("npx1 = %" PRIu64, npx1); + qDebug("npy1 = %" PRIu64, npy1); + qDebug("ipg2 = %" PRIu64, ipg2); + qDebug("npx2 = %" PRIu64, npx2); + qDebug("npy2 = %" PRIu64 "\n", npy2); + + if (n > 1) + loopNextPacketSet(x, n, 0, loopDelay); + else if (n == 0) + x = 0; + + for (uint j = 0; j < (x+y); j++) + { + + if (j == 0 || frameVariableCount > 1) + { + len = streamList_[i]->frameValue( + pktBuf_, sizeof(pktBuf_), j); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d) sec = %lu nsec = %lu", + i, j, sec, nsec); + + appendToPacketList(sec, nsec, pktBuf_, len); + + if ((j > 0) && (((j+1) % burstSize) == 0)) + { + nsec += (j < nb1) ? ibg1 : ibg2; + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + else + { + if (j < x) + nsec += (j < npx1) ? ipg1 : ipg2; + else + nsec += ((j-x) < npy1) ? ipg1 : ipg2; + + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + } + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, 0, + streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg1 : ipg1); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::updatePacketListInterleaved() +{ + int numStreams = 0; + quint64 minGap = ULLONG_MAX; + quint64 duration = quint64(1e9); + QList ibg1, ibg2; + QList nb1, nb2; + QList ipg1, ipg2; + QList np1, np2; + QList schedSec, schedNsec; + QList pktCount, burstCount; + QList burstSize; + QList isVariable; + QList pktBuf; + QList pktLen; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (!streamList_[i]->isEnabled()) + continue; + + double numBursts = 0; + double numPackets = 0; + + quint64 _burstSize; + double ibg = 0; + quint64 _ibg1 = 0, _ibg2 = 0; + quint64 _nb1 = 0, _nb2 = 0; + double ipg = 0; + quint64 _ipg1 = 0, _ipg2 = 0; + quint64 _np1 = 0, _np2 = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->burstRate(); + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + _ibg1 = quint64(ceil(ibg)); + _ibg2 = quint64(floor(ibg)); + _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); + _nb2 = quint64(numBursts) - _nb1; + _burstSize = streamList_[i]->burstSize(); + } + break; + case OstProto::StreamControl::e_su_packets: + numPackets = streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + _ipg1 = llrint(ceil(ipg)); + _ipg2 = quint64(floor(ipg)); + _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); + _np2 = quint64(numPackets) - _np1; + _burstSize = 1; + } + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, _ibg1); + qDebug("nb1 = %" PRIu64, _nb1); + qDebug("ibg2 = %" PRIu64, _ibg2); + qDebug("nb2 = %" PRIu64 "\n", _nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, _ipg1); + qDebug("np1 = %" PRIu64, _np1); + qDebug("ipg2 = %" PRIu64, _ipg2); + qDebug("np2 = %" PRIu64 "\n", _np2); + + + if (_ibg2 && (_ibg2 < minGap)) + minGap = _ibg2; + + if (_ibg1 && (_ibg1 > duration)) + duration = _ibg1; + + ibg1.append(_ibg1); + ibg2.append(_ibg2); + + nb1.append(_nb1); + nb2.append(_nb1); + + burstSize.append(_burstSize); + + if (_ipg2 && (_ipg2 < minGap)) + minGap = _ipg2; + + if (_np1) + { + if (_ipg1 && (_ipg1 > duration)) + duration = _ipg1; + } + else + { + if (_ipg2 && (_ipg2 > duration)) + duration = _ipg2; + } + + ipg1.append(_ipg1); + ipg2.append(_ipg2); + + np1.append(_np1); + np2.append(_np1); + + schedSec.append(0); + schedNsec.append(0); + + pktCount.append(0); + burstCount.append(0); + + if (streamList_[i]->isFrameVariable()) + { + isVariable.append(true); + pktBuf.append(QByteArray()); + pktLen.append(0); + } + else + { + isVariable.append(false); + pktBuf.append(QByteArray()); + pktBuf.last().resize(kMaxPktSize); + pktLen.append(streamList_[i]->frameValue( + (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); + } + + numStreams++; + } // for i + + qDebug("minGap = %" PRIu64, minGap); + qDebug("duration = %" PRIu64, duration); + + uchar* buf; + int len; + quint64 durSec = duration/ulong(1e9); + quint64 durNsec = duration % ulong(1e9); + quint64 sec = 0; + quint64 nsec = 0; + quint64 lastPktTxSec = 0; + quint64 lastPktTxNsec = 0; + do + { + for (int i = 0; i < numStreams; i++) + { + // If a packet is not scheduled yet, look at the next stream + if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) + continue; + + for (uint j = 0; j < burstSize[i]; j++) + { + if (isVariable.at(i)) + { + buf = pktBuf_; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), + pktCount[i]); + } + else + { + buf = (uchar*) pktBuf.at(i).data(); + len = pktLen.at(i); + } + + if (len <= 0) + continue; + + qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); + appendToPacketList(sec, nsec, buf, len); + lastPktTxSec = sec; + lastPktTxNsec = nsec; + + pktCount[i]++; + schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? + ipg1.at(i) : ipg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + burstCount[i]++; + schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? + ibg1.at(i) : ibg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + nsec += minGap; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } while ((sec < durSec) || (nsec < durNsec)); + + qint64 delaySec = durSec - lastPktTxSec; + qint64 delayNsec = durNsec - lastPktTxNsec; + while (delayNsec < 0) + { + delayNsec += long(1e9); + delaySec--; + } + qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); + setPacketListLoopMode(true, delaySec, delayNsec); + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? + stats_.rxPkts - epochStats_.rxPkts : + stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); + stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? + stats_.rxBytes - epochStats_.rxBytes : + stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? + stats_.txPkts - epochStats_.txPkts : + stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); + stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? + stats_.txBytes - epochStats_.txBytes : + stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; + + stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? + stats_.rxDrops - epochStats_.rxDrops : + stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); + stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? + stats_.rxErrors - epochStats_.rxErrors : + stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); + stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? + stats_.rxFifoErrors - epochStats_.rxFifoErrors : + stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); + stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? + stats_.rxFrameErrors - epochStats_.rxFrameErrors : + stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..44f0c1d --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 rxDrops; + quint64 rxErrors; + quint64 rxFifoErrors; + quint64 rxFrameErrors; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + bool isUsable() { return isUsable_; } + + virtual void init(); + + int id() { return data_.port_id().id(); } + const char* name() { return data_.name().c_str(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) = 0; + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, + quint64 secDelay, quint64 nsecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + void addNote(QString note); + + void updatePacketListSequential(); + void updatePacketListInterleaved(); + + bool isUsable_; + OstProto::Port data_; + OstProto::LinkState linkState_; + ulong minPacketSetSize_; + + quint64 maxStatsValue_; + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/bsdport.cpp b/server/bsdport.cpp new file mode 100644 index 0000000..4ac9ab7 --- /dev/null +++ b/server/bsdport.cpp @@ -0,0 +1,355 @@ +/* +Copyright (C) 2012 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 "bsdport.h" + +#ifdef Q_OS_BSD4 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#define ifr_flagshigh ifr_flags +#define IFF_PPROMISC (IFF_PROMISC << 16) +#endif + +QList BsdPort::allPorts_; +BsdPort::StatsMonitor *BsdPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +BsdPort::BsdPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Bsd + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = ULONG_MAX; +} + +BsdPort::~BsdPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if (ifr.ifr_flagshigh & promisc) + { + ifr.ifr_flagshigh &= ~promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + else + qDebug("Cleared promisc successfully"); + } + else + qDebug("clear_promisc is set but IFF_PPROMISC is not?"); + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void BsdPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +bool BsdPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool BsdPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +BsdPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; +} + +void BsdPort::StatsMonitor::run() +{ + int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + const int mibLen = sizeof(mib)/sizeof(mib[0]); + QHash portStats; + QHash linkState; + int sd; + QByteArray buf; + size_t len; + char *p, *end; + int count; + struct ifreq ifr; + + // + // We first setup stuff before we start polling for stats + // + if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); + return; + } + + qDebug("sysctl mib returns reqd len = %d\n", (int) len); + len *= 2; // for extra room, just in case! + buf.fill('\0', len); + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); + return; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + + // + // Populate the port stats hash table + // + p = buf.data(); + end = p + len; + count = 0; + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); + + if (ifm->ifm_type == RTM_IFINFO) + { + char ifname[1024]; + + strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + + qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); + foreach(BsdPort* port, allPorts_) + { + if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) + { + Q_ASSERT(ifm->ifm_index == sdl->sdl_index); + portStats[uint(ifm->ifm_index)] = &(port->stats_); + linkState[uint(ifm->ifm_index)] = &(port->linkState_); + + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if ((ifr.ifr_flagshigh & promisc) == 0) + { + ifr.ifr_flagshigh |= promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + { + qDebug("%s: set promisc successful", + port->name()); + port->clearPromisc_ = true; + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + else + qDebug("%s: promisc already set", port->name()); + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + count++; + } + p += ifm->ifm_msglen; + } + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in NET_RT_IFLIST - no stats will be available"); + return; + } + + close(sd); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); + goto _try_later; + } + + p = buf.data(); + end = p + len; + + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + AbstractPort::PortStats *stats; + + if (ifm->ifm_type != RTM_IFINFO) + goto _next; + + stats = portStats[ifm->ifm_index]; + if (stats) + { + struct if_data *ifd = &(ifm->ifm_data); + OstProto::LinkState *state = linkState[ifm->ifm_index]; + u_long in_packets; + + Q_ASSERT(state); +#ifdef Q_OS_MAC + *state = ifm->ifm_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; +#else + *state = (OstProto::LinkState) ifd->ifi_link_state; +#endif + + in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxPps = + ((in_packets >= stats->rxPkts) ? + in_packets - stats->rxPkts : + in_packets + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((ifd->ifi_ibytes >= stats->rxBytes) ? + ifd->ifi_ibytes - stats->rxBytes : + ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = in_packets; + stats->rxBytes = ifd->ifi_ibytes; + stats->txPps = + ((ifd->ifi_opackets >= stats->txPkts) ? + ifd->ifi_opackets - stats->txPkts : + ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((ifd->ifi_obytes >= stats->txBytes) ? + ifd->ifi_obytes - stats->txBytes : + ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = ifd->ifi_opackets; + stats->txBytes = ifd->ifi_obytes; + + stats->rxDrops = ifd->ifi_iqdrops; + stats->rxErrors = ifd->ifi_ierrors; + } +_next: + p += ifm->ifm_msglen; + } +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); +} + +void BsdPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/bsdport.h b/server/bsdport.h new file mode 100644 index 0000000..776c39a --- /dev/null +++ b/server/bsdport.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2012 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 +*/ + +#ifndef _SERVER_BSD_PORT_H +#define _SERVER_BSD_PORT_H + +#include + +#ifdef Q_OS_BSD4 + +#include "pcapport.h" + +class BsdPort : public PcapPort +{ +public: + BsdPort(int id, const char *device); + ~BsdPort(); + + void init(); + + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..8206ac2 --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,102 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +#include +#include + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + trayIcon_->hide(); + + delete trayIcon_; + delete trayIconMenu_; + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + QMessageBox::critical(0, qApp->applicationName(), + rpcServer->errorString()); + return false; + } + + trayIconMenu_ = new QMenu(this); + + trayIconMenu_->addAction(actionShow); + trayIconMenu_->addAction(actionExit); + trayIconMenu_->setDefaultAction(actionShow); + trayIcon_ = new QSystemTrayIcon(); + trayIcon_->setIcon(QIcon(":/icons/portgroup.png")); + trayIcon_->setToolTip(qApp->applicationName()); + trayIcon_->setContextMenu(trayIconMenu_); + trayIcon_->show(); + + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + connect(this, SIGNAL(hideMe(bool)), this, SLOT(setHidden(bool)), + Qt::QueuedConnection); + + return true; +} + +void Drone::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange && isMinimized()) + { + emit hideMe(true); + event->ignore(); + return; + } + + QWidget::changeEvent(event); +} + +void Drone::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + showNormal(); + activateWindow(); + } +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..7466a76 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include "ui_drone.h" + +#include +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QWidget, Ui::Drone +{ + Q_OBJECT + +public: + Drone(QWidget *parent = 0); + ~Drone(); + bool init(); + +signals: + void hideMe(bool hidden); + +protected: + void changeEvent(QEvent *event); + +private: + QSystemTrayIcon *trayIcon_; + QMenu *trayIconMenu_; + RpcServer *rpcServer; + OstProto::OstService *service; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..341634c --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,48 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lm +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += drone.qrc +HEADERS += drone.h +FORMS += drone.ui +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + bsdport.cpp \ + linuxport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone.qrc b/server/drone.qrc new file mode 100644 index 0000000..a642656 --- /dev/null +++ b/server/drone.qrc @@ -0,0 +1,5 @@ + + + icons/portgroup.png + + diff --git a/server/drone.ui b/server/drone.ui new file mode 100644 index 0000000..e2e0613 --- /dev/null +++ b/server/drone.ui @@ -0,0 +1,190 @@ + + Drone + + + + 0 + 0 + 268 + 216 + + + + Drone + + + :/icons/portgroup.png + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <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;"><span style=" font-size:29pt; font-weight:600;">Ostinato</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + (Server) + + + Qt::AlignCenter + + + + + + + TODO: Info/Status here + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Exit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show + + + + + Exit + + + + + + + + + pushButton + clicked() + actionExit + trigger() + + + 134 + 194 + + + -1 + -1 + + + + + actionShow + triggered() + Drone + showNormal() + + + -1 + -1 + + + 133 + 107 + + + + + actionExit + triggered() + Drone + close() + + + -1 + -1 + + + 133 + 107 + + + + + diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..64763cb --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,81 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +#include + +#ifdef Q_OS_UNIX +#include +#endif + +extern ProtocolManager *OstProtocolManager; + +int myport; + +void cleanup(int /*signum*/) +{ + qApp->exit(-1); +} + +int main(int argc, char *argv[]) +{ + int exitCode = 0; + QApplication app(argc, argv); + Drone *drone = new Drone(); + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone->objectName()); + + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone->init()) + { + exitCode = -1; + goto _exit; + } + +#ifdef Q_OS_UNIX + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup; + if (sigaction(SIGTERM, &sa, NULL)) + qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); + if (sigaction(SIGINT, &sa, NULL)) + qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); +#endif + + drone->setWindowFlags(drone->windowFlags() + | Qt::WindowMaximizeButtonHint + | Qt::WindowMinimizeButtonHint); + drone->showMinimized(); + exitCode = app.exec(); + +_exit: + delete drone; + delete OstProtocolManager; + + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + +#include "linuxport.h" + +#ifdef Q_OS_LINUX + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QList LinuxPort::allPorts_; +LinuxPort::StatsMonitor *LinuxPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +LinuxPort::LinuxPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Linux + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = 0xffffffff; +} + +LinuxPort::~LinuxPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if (ifr.ifr_flags & IFF_PROMISC) + { + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + } + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void LinuxPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +OstProto::LinkState LinuxPort::linkState() +{ + return linkState_; +} + +bool LinuxPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool LinuxPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +LinuxPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; + ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(ioctlSocket_ >= 0); +} + +LinuxPort::StatsMonitor::~StatsMonitor() +{ + close(ioctlSocket_); +} + +void LinuxPort::StatsMonitor::run() +{ + if (netlinkStats() < 0) + { + qDebug("netlink stats not available - using /proc stats"); + procStats(); + } +} + +void LinuxPort::StatsMonitor::procStats() +{ + PortStats **portStats; + int fd; + QByteArray buf; + int len; + char *p, *end; + int count, index; + const char* fmtopt[] = { + "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", + }; + const char *fmt; + + // + // We first setup stuff before we start polling for stats + // + fd = open("/proc/net/dev", O_RDONLY); + if (fd < 0) + { + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; + } + + buf.fill('\0', 8192); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + qWarning("initial buffer size is too small. no stats will be available"); + return; + } + + p = buf.data(); + end = p + len; + + // Select scanf format + if (strstr(buf, "compressed")) + fmt = fmtopt[0]; + else + fmt = fmtopt[1]; + + // Count number of lines - number of ports is 2 less than number of lines + count = 0; + while (p < end) + { + if (*p == '\n') + count++; + p++; + } + count -= 2; + + if (count <= 0) + { + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; + } + + portStats = (PortStats**) calloc(count, sizeof(PortStats)); + Q_ASSERT(portStats != NULL); + + // + // Populate the port stats array + // + p = buf.data(); + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + char* q; + + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; + + q = p; + + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) + { + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; + p++; + } + Q_ASSERT(index == count); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; + } + + p = buf.data(); + end = p + len; + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 rxErrors, rxDrops, rxFifo, rxFrame; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, + &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + stats->rxPps = + ((rxPkts >= stats->rxPkts) ? + rxPkts - stats->rxPkts : + rxPkts + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rxBytes >= stats->rxBytes) ? + rxBytes - stats->rxBytes : + rxBytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = + ((txPkts >= stats->txPkts) ? + txPkts - stats->txPkts : + txPkts + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((txBytes >= stats->txBytes) ? + txBytes - stats->txBytes : + txBytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + + stats->rxDrops = rxDrops; + stats->rxErrors = rxErrors; + stats->rxFifoErrors = rxFifo; + stats->rxFrameErrors = rxFrame; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); + } + + free(portStats); +} + +int LinuxPort::StatsMonitor::netlinkStats() +{ + QHash portStats; + QHash linkState; + int fd; + struct sockaddr_nl local; + struct sockaddr_nl kernel; + QByteArray buf; + int len, count; + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtg; + } ifListReq; + struct iovec iov; + struct msghdr msg; + struct nlmsghdr *nlm; + bool done = false; + + // + // We first setup stuff before we start polling for stats + // + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + qWarning("Unable to open netlink socket (errno %d)", errno); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) + { + qWarning("Unable to bind netlink socket (errno %d)", errno); + return -1; + } + + memset(&ifListReq, 0, sizeof(ifListReq)); + ifListReq.nlh.nlmsg_len = sizeof(ifListReq); + ifListReq.nlh.nlmsg_type = RTM_GETLINK; + ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifListReq.nlh.nlmsg_pid = 0; + ifListReq.rtg.rtgen_family = AF_PACKET; + + buf.fill('\0', 1024); + + msg.msg_name = &kernel; + msg.msg_namelen = sizeof(kernel); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); + + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + return -1; + } + + // Find required size of buffer and resize accordingly + while (1) + { + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + msg.msg_flags = 0; + + // Peek at reply to check buffer size required + len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink closed the socket on my face!"); + return -1; + } + else + { + if (msg.msg_flags & MSG_TRUNC) + { + if (len == buf.size()) // Older Kernel returns truncated size + { + qDebug("netlink buffer size %d not enough", buf.size()); + qDebug("retrying with double the size"); + // Double the size and retry + buf.resize(buf.size()*2); + continue; + } + else // Newer Kernel returns actual size required + { + qDebug("netlink required buffer size = %d", len); + buf.resize(len); + continue; + } + } + else + qDebug("buffer size %d enough for netlink", buf.size()); + + break; + } + } + + count = 0; + +_retry: + msg.msg_flags = 0; + + // Actually receive the reply now + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry; + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + return -1; + } + + // + // Populate the port stats hash table + // + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + char ifname[64] = ""; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error %d", err->error); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == IFLA_IFNAME) + { + strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); + ifname[RTA_PAYLOAD(rta)] = 0; + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + + qDebug("if: %s(%d)", ifname, ifi->ifi_index); + foreach(LinuxPort* port, allPorts_) + { + if (strcmp(port->name(), ifname) == 0) + { + portStats[uint(ifi->ifi_index)] = &(port->stats_); + linkState[uint(ifi->ifi_index)] = &(port->linkState_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + count++; + break; + } + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry; + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); + return - 1; + } + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + goto _try_later; + } + + done = false; + +_retry_recv: + msg.msg_flags = 0; + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry_recv; + qWarning("netlink recv error %d", errno); + break; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + break; + } + + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error: %s", strerror(-err->error)); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + // TODO: IFLA_STATS64 + if (rta->rta_type == IFLA_STATS) + { + struct rtnl_link_stats *rtnlStats = + (struct rtnl_link_stats*) RTA_DATA(rta); + AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + OstProto::LinkState *state = linkState[ifi->ifi_index]; + + if (!stats) + break; + + stats->rxPps = + ((rtnlStats->rx_packets >= stats->rxPkts) ? + rtnlStats->rx_packets - stats->rxPkts : + rtnlStats->rx_packets + (kMaxValue32 + - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rtnlStats->rx_bytes >= stats->rxBytes) ? + rtnlStats->rx_bytes - stats->rxBytes : + rtnlStats->rx_bytes + (kMaxValue32 + - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rtnlStats->rx_packets; + stats->rxBytes = rtnlStats->rx_bytes; + stats->txPps = + ((rtnlStats->tx_packets >= stats->txPkts) ? + rtnlStats->tx_packets - stats->txPkts : + rtnlStats->tx_packets + (kMaxValue32 + - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((rtnlStats->tx_bytes >= stats->txBytes) ? + rtnlStats->tx_bytes - stats->txBytes : + rtnlStats->tx_bytes + (kMaxValue32 + - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = rtnlStats->tx_packets; + stats->txBytes = rtnlStats->tx_bytes; + + // TODO: export detailed error stats + stats->rxDrops = rtnlStats->rx_dropped + + rtnlStats->rx_missed_errors; + stats->rxErrors = rtnlStats->rx_errors; + stats->rxFifoErrors = rtnlStats->rx_fifo_errors; + stats->rxFrameErrors = rtnlStats->rx_crc_errors + + rtnlStats->rx_length_errors + + rtnlStats->rx_over_errors + + rtnlStats->rx_frame_errors; + + Q_ASSERT(state); + *state = ifi->ifi_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; + + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry_recv; + +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); + + return 0; +} + +int LinuxPort::StatsMonitor::setPromisc(const char * portName) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); + + if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) + { + return 1; + } + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + portName, strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + portName, strerror(errno)); + } + + return 0; +} + +void LinuxPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/linuxport.h b/server/linuxport.h new file mode 100644 index 0000000..2658560 --- /dev/null +++ b/server/linuxport.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SERVER_LINUX_PORT_H +#define _SERVER_LINUX_PORT_H + +#include + +#ifdef Q_OS_LINUX + +#include "pcapport.h" + +class LinuxPort : public PcapPort +{ +public: + LinuxPort(int id, const char *device); + ~LinuxPort(); + + void init(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + ~StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + int netlinkStats(); + void procStats(); + int setPromisc(const char* portName); + + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + int ioctlSocket_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/myservice.cpp b/server/myservice.cpp new file mode 100644 index 0000000..be0984a --- /dev/null +++ b/server/myservice.cpp @@ -0,0 +1,475 @@ +/* +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 +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + + s->set_rx_drops(stats.rxDrops); + s->set_rx_errors(stats.rxErrors); + s->set_rx_fifo_errors(stats.rxFifoErrors); + s->set_rx_frame_errors(stats.rxFrameErrors); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..5244956 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,788 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + + +#if defined(Q_OS_LINUX) +typedef struct timeval TimeStamp; +static void inline getTimeStamp(TimeStamp *stamp) +{ + gettimeofday(stamp, NULL); +} + +// Returns time diff in usecs between end and start +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + struct timeval diff; + long usecs; + + timersub(end, start, &diff); + + usecs = diff.tv_usec; + if (diff.tv_sec) + usecs += diff.tv_sec*1e6; + + return usecs; +} +#elif defined(Q_OS_WIN32) +static quint64 gTicksFreq; +typedef LARGE_INTEGER TimeStamp; +static void inline getTimeStamp(TimeStamp* stamp) +{ + QueryPerformanceCounter(stamp); +} + +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + if (end->QuadPart >= start->QuadPart) + return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; + else + { + // FIXME: incorrect! what's the max value for this counter before + // it rolls over? + return (start->QuadPart)*long(1e6)/gTicksFreq; + } +} +#else +typedef int TimeStamp; +static void inline getTimeStamp(TimeStamp*) {} +static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } +#endif + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitorRx_) + monitorRx_->stop(); + if (monitorTx_) + monitorTx_->stop(); + + delete capturer_; + delete transmitter_; + + if (monitorRx_) + monitorRx_->wait(); + delete monitorRx_; + + if (monitorTx_) + monitorTx_->wait(); + delete monitorTx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + bool noLocalCapture; + + direction_ = direction; + isDirectional_ = true; + isPromisc_ = true; + noLocalCapture = true; + stats_ = stats; + stop_ = false; + +_retry: +#ifdef Q_OS_WIN32 + int flags = 0; + + if (isPromisc_) + flags |= PCAP_OPENFLAG_PROMISCUOUS; + if (noLocalCapture) + flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; + + handle_ = pcap_open(device, 64 /* FIXME */, flags, + 1000 /* ms */, NULL, errbuf); +#else + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), + 1000 /* ms */, errbuf); +#endif + + if (handle_ == NULL) + { + if (isPromisc_ && QString(errbuf).contains("promiscuous")) + { + qDebug("Can't set promiscuous mode, trying non-promisc %s", device); + isPromisc_ = false; + goto _retry; + } + else if (noLocalCapture && QString(errbuf).contains("loopback")) + { + qDebug("Can't set no local capture mode %s", device); + noLocalCapture = false; + goto _retry; + } + else + goto _open_error; + } +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); +} + +PcapPort::PortMonitor::~PortMonitor() +{ + if (handle_) + pcap_close(handle_); +} + +void PcapPort::PortMonitor::run() +{ + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +void PcapPort::PortMonitor::stop() +{ + stop_ = true; + pcap_breakloop(handle()); +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE] = ""; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + gTicksFreq = ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; + if (usingInternalHandle_) + pcap_close(handle_); +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for packetSequenceList + while(packetSequenceList_.size()) + delete packetSequenceList_.takeFirst(); + + currentPacketSequence_ = NULL; + repeatSequenceStart_ = -1; + repeatSize_ = 0; + packetCount_ = 0; + + returnToQIdx_ = -1; + + setPacketListLoopMode(false, 0, 0); +} + +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) +{ + currentPacketSequence_ = new PacketSequence; + currentPacketSequence_->repeatCount_ = repeats; + currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + + repeatDelayNsec/1000; + + repeatSequenceStart_ = packetSequenceList_.size(); + repeatSize_ = size; + packetCount_ = 0; + + packetSequenceList_.append(currentPacketSequence_); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = nsec/1000; + + if (currentPacketSequence_ == NULL || + !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) + { + if (currentPacketSequence_ != NULL) + { + long usecs; + + usecs = (pktHdr.ts.tv_sec + - currentPacketSequence_->lastPacket_->ts.tv_sec) + * long(1e6); + usecs += (pktHdr.ts.tv_usec + - currentPacketSequence_->lastPacket_->ts.tv_usec); + currentPacketSequence_->usecDelay_ = usecs; + } + + //! \todo (LOW): calculate sendqueue size + currentPacketSequence_ = new PacketSequence; + + packetSequenceList_.append(currentPacketSequence_); + + // Validate that the pkt will fit inside the new currentSendQueue_ + Q_ASSERT(currentPacketSequence_->hasFreeSpace( + sizeof(pcap_pkthdr) + length)); + } + + if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) + { + op = false; + } + + packetCount_++; + if (repeatSize_ > 0 && packetCount_ == repeatSize_) + { + qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", + repeatSequenceStart_, repeatSize_); + + // Set the packetSequence repeatSize + Q_ASSERT(repeatSequenceStart_ >= 0); + Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); + + if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) + { + PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; + + currentPacketSequence_->usecDelay_ = start->usecDelay_; + start->usecDelay_ = 0; + start->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + } + + repeatSize_ = 0; + + // End current pktSeq and trigger a new pktSeq allocation for next pkt + currentPacketSequence_ = NULL; + } + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalHandle_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + long overHead = 0; // overHead should be negative or zero + + qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); + if (packetSequenceList_.size() <= 0) + return; + + for(i = 0; i < packetSequenceList_.size(); i++) { + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, + packetSequenceList_.at(i)->repeatCount_, + packetSequenceList_.at(i)->repeatSize_, + packetSequenceList_.at(i)->usecDelay_); + qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, + packetSequenceList_.at(i)->packets_, + packetSequenceList_.at(i)->usecDuration_); + } + + for(i = 0; i < packetSequenceList_.size(); i++) + { + +_restart: + int rptSz = packetSequenceList_.at(i)->repeatSize_; + int rptCnt = packetSequenceList_.at(i)->repeatCount_; + + for (int j = 0; j < rptCnt; j++) + { + for (int k = 0; k < rptSz; k++) + { + int ret; + PacketSequence *seq = packetSequenceList_.at(i+k); +#ifdef Q_OS_WIN32 + TimeStamp ovrStart, ovrEnd; + + if (seq->usecDuration_ <= long(1e6)) // 1s + { + getTimeStamp(&ovrStart); + ret = pcap_sendqueue_transmit(handle_, + seq->sendQueue_, kSyncTransmit); + if (ret >= 0) + { + stats_->txPkts += seq->packets_; + stats_->txBytes += seq->bytes_; + + getTimeStamp(&ovrEnd); + overHead += seq->usecDuration_ + - udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + } + if (stop_) + ret = -2; + } + else + { + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); + } +#else + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); +#endif + + if (ret >= 0) + { + long usecs = seq->usecDelay_ + overHead; + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + } + else + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + return; + } + } + } + } + + if (returnToQIdx_ >= 0) + { + long usecs = loopDelay_ + overHead; + + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + + i = returnToQIdx_; + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, long &overHead, int sync) +{ + TimeStamp ovrStart, ovrEnd; + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + getTimeStamp(&ovrStart); + while((char*) hdr < end) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + getTimeStamp(&ovrEnd); + + overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + usec += overHead; + if (usec > 0) + { + udelay(usec); + overHead = 0; + } + else + overHead = usec; + + ts = hdr->ts; + getTimeStamp(&ovrStart); + } + + Q_ASSERT(pktLen > 0); + + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) (pkt + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + if (stop_) + { + return -2; + } + } + + return 0; +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#if defined(Q_OS_WIN32) + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#elif defined(Q_OS_LINUX) + struct timeval delay, target, now; + + //qDebug("usec delay = %ld", usec); + + delay.tv_sec = 0; + delay.tv_usec = usec; + + while (delay.tv_usec >= 1000000) + { + delay.tv_sec++; + delay.tv_usec -= 1000000; + } + + gettimeofday(&now, NULL); + timeradd(&now, &delay, &target); + + do { + gettimeofday(&now, NULL); + } while (timercmp(&now, &target, <)); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } +_retry: + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + flag, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..d05018b --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0, 0); + } + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { + transmitter_->loopNextPacketSet(size, repeats, + repeatDelaySec, repeatDelayNsec); + } + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, nsec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) + { + transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + ~PortMonitor(); + void run(); + void stop(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } + protected: + AbstractPort::PortStats *stats_; + bool stop_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + bool isPromisc_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + + class PacketSequence + { + public: + PacketSequence() { + sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + lastPacket_ = NULL; + packets_ = 0; + bytes_ = 0; + usecDuration_ = 0; + repeatCount_ = 1; + repeatSize_ = 1; + usecDelay_ = 0; + } + ~PacketSequence() { + pcap_sendqueue_destroy(sendQueue_); + } + bool hasFreeSpace(int size) { + if ((sendQueue_->len + size) <= sendQueue_->maxlen) + return true; + else + return false; + } + int appendPacket(const struct pcap_pkthdr *pktHeader, + const uchar *pktData) { + if (lastPacket_) + { + usecDuration_ += (pktHeader->ts.tv_sec + - lastPacket_->ts.tv_sec) * long(1e6); + usecDuration_ += (pktHeader->ts.tv_usec + - lastPacket_->ts.tv_usec); + } + packets_++; + bytes_ += pktHeader->caplen; + lastPacket_ = (struct pcap_pkthdr *) + (sendQueue_->buffer + sendQueue_->len); + return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); + } + pcap_send_queue *sendQueue_; + struct pcap_pkthdr *lastPacket_; + long packets_; + long bytes_; + ulong usecDuration_; + int repeatCount_; + int repeatSize_; + long usecDelay_; + }; + + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, + int sync); + + quint64 ticksFreq_; + QList packetSequenceList_; + PacketSequence *currentPacketSequence_; + int repeatSequenceStart_; + quint64 repeatSize_; + quint64 packetCount_; + + int returnToQIdx_; + quint64 loopDelay_; + + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..2981464 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2010-2012 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 "portmanager.h" + +#include +#include + +#include "bsdport.h" +#include "linuxport.h" +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + +#if defined(Q_OS_WIN32) + port = new WinPcapPort(i, device->name); +#elif defined(Q_OS_LINUX) + port = new LinuxPort(i, device->name); +#elif defined(Q_OS_BSD4) + port = new BsdPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + + portList_.append(port); + } + + pcap_freealldevs(deviceList); + + foreach(AbstractPort *port, portList_) + port->init(); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..bbd49d8 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,226 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + monitorRx_->stop(); + monitorTx_->stop(); + monitorRx_->wait(); + monitorTx_->wait(); + + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 256; +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + if (handle()) + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + if (!stop_) + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..d5e8af1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,109 @@ + +#include "ostprotolib.h" +#include "pcapfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" +#include "settings.h" + +#include +#include +#include +#include + +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int /*argc*/, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) +{ + bool isOk; + QString error; + + if (argc != 3) + { + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; + } + + OstProto::StreamConfigList streams; + QString inFile(argv[2]); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) + { + printf("%s: %s\n", + inFile.toAscii().constData(), error.toAscii().constData()); + } + + if (!isOk) + return 1; + + return 0; +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..8dbe0cb --- /dev/null +++ b/test/test.pro @@ -0,0 +1,34 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" "../client" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 + +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..718ce69 --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.5.1 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From 05cf3bdc5f3c9c9057e81140f278138e4724f632 Mon Sep 17 00:00:00 2001 From: Giorgio Buffa Date: Fri, 22 Nov 2013 19:05:34 +0530 Subject: [PATCH 208/294] A "packet set" can span multiple PacketSequences - the outermost loop during transmission should increment in units of PacketSet rather than PacketSequence Fixes issue 116 --- server/pcapport.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 5244956..94ac735 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -502,7 +502,8 @@ void PcapPort::PortTransmitter::run() packetSequenceList_.at(i)->usecDuration_); } - for(i = 0; i < packetSequenceList_.size(); i++) + i = 0; + while (i < packetSequenceList_.size()) { _restart: @@ -566,6 +567,9 @@ _restart: } } } + + // Move to the next Packet Set + i += rptSz; } if (returnToQIdx_ >= 0) From 925697dfcc13fb2f4e46e19602ee4935a66c9228 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 3 Mar 2014 20:30:42 +0530 Subject: [PATCH 209/294] NOX: Removed GUI stuff from the drone app. Changes may not compile --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 +++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 88 ++ client/mainwindow.cpp | 135 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 +++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 88 ++ client/ostinato.qrc | 38 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 573 ++++++++++ client/port.h | 147 +++ client/portconfigdialog.cpp | 57 + client/portconfigdialog.h | 39 + client/portconfigdialog.ui | 109 ++ client/portgroup.cpp | 838 ++++++++++++++ client/portgroup.h | 145 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 339 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 +++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 320 ++++++ client/portstatsmodel.h | 151 +++ client/portstatswindow.cpp | 180 +++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 +++ client/portswindow.cpp | 663 +++++++++++ client/portswindow.h | 86 ++ client/portswindow.ui | 299 +++++ client/preferences.cpp | 119 ++ client/preferences.h | 45 + client/preferences.ui | 226 ++++ client/settings.h | 86 ++ client/stream.cpp | 79 ++ client/stream.h | 42 + client/streamconfigdialog.cpp | 1143 +++++++++++++++++++ client/streamconfigdialog.h | 146 +++ client/streamconfigdialog.ui | 1462 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 288 +++++ client/streammodel.h | 78 ++ common/abstractfileformat.cpp | 127 +++ common/abstractfileformat.h | 91 ++ common/abstractprotocol.cpp | 928 ++++++++++++++++ common/abstractprotocol.h | 167 +++ common/arp.cpp | 993 +++++++++++++++++ common/arp.h | 121 ++ common/arp.proto | 67 ++ common/arp.ui | 518 +++++++++ common/comboprotocol.h | 223 ++++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot3.cpp | 239 ++++ common/dot3.h | 80 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/eth2.cpp | 231 ++++ common/eth2.h | 78 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/fileformat.cpp | 483 ++++++++ common/fileformat.h | 62 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 1055 ++++++++++++++++++ common/gmp.h | 163 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 ++++++++++++++ common/hexdump.cpp | 263 +++++ common/hexdump.h | 89 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/icmp.cpp | 594 ++++++++++ common/icmp.h | 117 ++ common/icmp.proto | 44 + common/icmp.ui | 178 +++ common/igmp.cpp | 449 ++++++++ common/igmp.h | 111 ++ common/igmp.proto | 27 + common/intcombobox.h | 69 ++ common/ip4.cpp | 752 +++++++++++++ common/ip4.h | 116 ++ common/ip4.proto | 66 ++ common/ip4.ui | 516 +++++++++ common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip6.cpp | 953 ++++++++++++++++ common/ip6.h | 131 +++ common/ip6.proto | 61 ++ common/ip6.ui | 467 ++++++++ common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/iputils.h | 122 +++ common/ipv4addressdelegate.h | 58 + common/ipv6addressdelegate.h | 60 + common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 331 ++++++ common/llc.h | 86 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/mac.cpp | 329 ++++++ common/mac.h | 91 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/mld.cpp | 624 +++++++++++ common/mld.h | 108 ++ common/mld.proto | 27 + common/ostproto.pro | 142 +++ common/ostprotolib.cpp | 55 + common/ostprotolib.h | 42 + common/payload.cpp | 284 +++++ common/payload.h | 85 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/pcapfileformat.cpp | 661 +++++++++++ common/pcapfileformat.h | 87 ++ common/pcapfileimport.ui | 132 +++ common/pdmlfileformat.cpp | 163 +++ common/pdmlfileformat.h | 42 + common/pdmlprotocol.cpp | 153 +++ common/pdmlprotocol.h | 64 ++ common/pdmlprotocols.cpp | 1357 +++++++++++++++++++++++ common/pdmlprotocols.h | 313 ++++++ common/pdmlreader.cpp | 533 +++++++++ common/pdmlreader.h | 75 ++ common/protocol.proto | 249 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 212 ++++ common/protocolmanager.h | 57 + common/sample.cpp | 546 +++++++++ common/sample.h | 103 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/snap.cpp | 307 ++++++ common/snap.h | 82 ++ common/snap.proto | 34 + common/snap.ui | 122 +++ common/streambase.cpp | 565 ++++++++++ common/streambase.h | 150 +++ common/svlan.cpp | 68 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/tcp.cpp | 709 ++++++++++++ common/tcp.h | 101 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/textproto.cpp | 295 +++++ common/textproto.h | 91 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/udp.cpp | 500 +++++++++ common/udp.h | 88 ++ common/udp.proto | 39 + common/udp.ui | 174 +++ common/userscript.cpp | 630 +++++++++++ common/userscript.h | 190 ++++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/vlan.cpp | 257 +++++ common/vlan.h | 82 ++ common/vlan.proto | 34 + common/vlan.ui | 179 +++ common/vlanstack.h | 30 + common/vlanstack.proto | 31 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 12 + extra/qhexedit2/src/commands.cpp | 115 ++ extra/qhexedit2/src/commands.h | 70 ++ extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 152 +++ extra/qhexedit2/src/qhexedit.h | 205 ++++ extra/qhexedit2/src/qhexedit_p.cpp | 800 ++++++++++++++ extra/qhexedit2/src/qhexedit_p.h | 120 ++ extra/qhexedit2/src/xbytearray.cpp | 167 +++ extra/qhexedit2/src/xbytearray.h | 66 ++ install.pri | 14 + ost.pro | 8 + protobuf.pri | 33 + rpc/pbhelper.h | 170 +++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 338 ++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 39 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 291 +++++ rpc/rpcserver.h | 66 ++ server/abstractport.cpp | 594 ++++++++++ server/abstractport.h | 127 +++ server/bsdport.cpp | 355 ++++++ server/bsdport.h | 61 ++ server/drone.cpp | 53 + server/drone.h | 40 + server/drone.pro | 47 + server/drone_main.cpp | 83 ++ server/icons/portgroup.png | Bin 0 -> 667 bytes server/linuxport.cpp | 769 +++++++++++++ server/linuxport.h | 68 ++ server/myservice.cpp | 475 ++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 788 +++++++++++++ server/pcapport.h | 217 ++++ server/portmanager.cpp | 94 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 226 ++++ server/winpcapport.h | 56 + test/main.cpp | 109 ++ test/test.pro | 34 + version.pri | 19 + 271 files changed, 45946 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portconfigdialog.cpp create mode 100644 client/portconfigdialog.h create mode 100644 client/portconfigdialog.ui create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/comboprotocol.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/ostproto.pro create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h create mode 100644 common/pcapfileimport.ui create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlprotocols.cpp create mode 100644 common/pdmlprotocols.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/commands.cpp create mode 100644 extra/qhexedit2/src/commands.h create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 extra/qhexedit2/src/xbytearray.cpp create mode 100644 extra/qhexedit2/src/xbytearray.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/bsdport.cpp create mode 100644 server/bsdport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/linuxport.cpp create mode 100644 server/linuxport.h create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 test/main.cpp create mode 100644 test/test.pro create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..4727324 --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2012 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
    G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/ostprotolib.h" +#include "../common/protocolmanager.h" +#include "settings.h" + +#include +#include +#include + +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; +QMainWindow *mainWindow; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + + delete mainWindow; + delete appSettings; + delete OstProtocolManager; + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..03a16ab --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,135 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "settings.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); + statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ + delete pgl; + localServer_->terminate(); + localServer_->waitForFinished(); + delete localServer_; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..0967390 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,88 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portconfigdialog.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portconfigdialog.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portconfigdialog.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..6162ac0 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,38 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/gaps.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..7f1b4c9 --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,573 @@ +/* +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 +*/ + +#include "port.h" + +#include "abstractfileformat.h" + +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; + +uint Port::mAllocStreamId = 0; + +static const int kEthOverhead = 20; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +void Port::recalculateAverageRates() +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAveragePacketRate(double packetsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) * + (packetsPerSec - avgPacketsPerSec_)); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); +} + +void Port::setAverageBitRate(double bitsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); + qDebug("rate = %g", rate); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) + * ((bitsPerSec - avgBitsPerSec_) + / ((s->frameLenAvg()+kEthOverhead)*8))); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + emit portRateChanged(mPortGroupId, mPortId); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + bool ret = false; + QDialog *optDialog; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); + OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); + + if (fmt == NULL) + goto _fail; + + if ((optDialog = fmt->openOptionsDialog())) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) + goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); + + if (!append) + { + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + } + + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); + for (int i = 0; i < streams.stream_size(); i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + +_user_cancel: + emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: + ret = true; + +_fail: + progress.close(); + mainWindow->setEnabled(true); + recalculateAverageRates(); + return ret; +} + +bool Port::saveStreams(QString fileName, QString fileType, QString &error) +{ + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); + OstProto::StreamConfigList streams; + + if (fmt == NULL) + goto _fail; + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..10c2803 --- /dev/null +++ b/client/port.h @@ -0,0 +1,147 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + double avgPacketsPerSec_; + double avgBitsPerSec_; + int numActiveStreams_; + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + OstProto::TransmitMode transmitMode() + { return d.transmit_mode(); } + double averagePacketRate() + { return avgPacketsPerSec_; } + double averageBitRate() + { return avgBitsPerSec_; } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void setAveragePacketRate(double packetsPerSec); + void setAverageBitRate(double bitsPerSec); + // FIXME(MED): Bad Hack! port should not need an external trigger to + // recalculate - refactor client side domain objects and model objects + void recalculateAverageRates(); + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); + +signals: + void portRateChanged(int portGroupId, int portId); + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portconfigdialog.cpp b/client/portconfigdialog.cpp new file mode 100644 index 0000000..17adeb5 --- /dev/null +++ b/client/portconfigdialog.cpp @@ -0,0 +1,57 @@ +/* +Copyright (C) 2011 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 "portconfigdialog.h" + +PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent) + : QDialog(parent), portConfig_(portConfig) +{ + qDebug("In %s", __FUNCTION__); + + setupUi(this); + + switch(portConfig_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + sequentialStreamsButton->setChecked(true); + break; + case OstProto::kInterleavedTransmit: + interleavedStreamsButton->setChecked(true); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } + + exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); +} + +void PortConfigDialog::accept() +{ + if (sequentialStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kSequentialTransmit); + else if (interleavedStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit); + else + Q_ASSERT(false); // Unreachable!!! + + portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked()); + + QDialog::accept(); +} diff --git a/client/portconfigdialog.h b/client/portconfigdialog.h new file mode 100644 index 0000000..4d14b0b --- /dev/null +++ b/client/portconfigdialog.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PORT_CONFIG_DIALOG_H +#define _PORT_CONFIG_DIALOG_H + +#include "ui_portconfigdialog.h" +#include "protocol.pb.h" +#include + +class PortConfigDialog : public QDialog, public Ui::PortConfigDialog +{ +public: + PortConfigDialog(OstProto::Port &portConfig, QWidget *parent); + +private: + virtual void accept(); + + OstProto::Port &portConfig_; +}; + +#endif + diff --git a/client/portconfigdialog.ui b/client/portconfigdialog.ui new file mode 100644 index 0000000..954b827 --- /dev/null +++ b/client/portconfigdialog.ui @@ -0,0 +1,109 @@ + + PortConfigDialog + + + + 0 + 0 + 244 + 160 + + + + Port Config + + + + + + Transmit Mode + + + + + + Sequential Streams + + + true + + + + + + + Interleaved Streams + + + + + + + + + + Exclusive Control + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PortConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..9762fac --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,838 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://code.google.com/p/ostinato/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->CopyFrom(portConfig); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + + emit portGroupDataChanged(mPortGroupId, id); + } + + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..b4d7580 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,145 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { reconnect = true; rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, OstProto::Port portConfig); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..0ef055e --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,339 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + qDebug("portGroupId = %d, portId = %d", portGroupId, portId); + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..ff9d7b7 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,320 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + case e_STAT_RX_DROPS : return quint64(stats.rx_drops()); + case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); + case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); + case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors()); + + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2").arg(portGroupIdx).arg(portIdx); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..a0d6868 --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + // Rx Errors + e_STAT_RX_DROPS, + e_STAT_RX_ERRORS, + e_STAT_RX_FIFO_ERRORS, + e_STAT_RX_FRAME_ERRORS, + + e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif + << "Receive Drops" + << "Receive Errors" + << "Receive Fifo Errors" + << "Receive Frame Errors" +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..1f3bc4d --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,663 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include "abstractfileformat.h" +#include "portconfigdialog.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +#include +#include +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + tvPortList->addAction(actionPort_Configuration); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + connect(plm->getStreamModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(streamModelDataChanged())); + connect(plm->getStreamModel(), + SIGNAL(modelReset()), + this, SLOT(streamModelDataChanged())); +} + +void PortsWindow::streamModelDataChanged() +{ + if (plm->isPort(tvPortList->currentIndex())) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + int ret; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + ret = scd->exec(); + + if (ret == QDialog::Accepted) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); + + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + qDebug("In %s", __FUNCTION__); + + if (previous.isValid() && plm->isPort(previous)) + { + disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), + this, SLOT(updatePortRates())); + } + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + updatePortRates(); + connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), + SLOT(updatePortRates())); + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + qDebug("In %s", __FUNCTION__); +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + // Update UI to reflect potential change in exclusive mode, + // transmit mode et al + when_portView_currentChanged(tvPortList->currentIndex(), + tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::on_averagePacketsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); + + plm->port(current).setAveragePacketRate(pps); +} + +void PortsWindow::on_averageBitsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); + + plm->port(current).setAverageBitRate(bps); +} + +void PortsWindow::updatePortRates() +{ + QModelIndex current = tvPortList->currentIndex(); + + if (!current.isValid()) + return; + + if (!plm->isPort(current)) + return; + + averagePacketsPerSec->setText(QString("%L1") + .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); + averageBitsPerSec->setText(QString("%L1") + .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + actionPort_Configuration->setEnabled(true); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + { + OstProto::Port config; + + config.set_is_exclusive_control(checked); + plm->portGroup(current.parent()).modifyPort(current.row(), config); + } +} + +void PortsWindow::on_actionPort_Configuration_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (!plm->isPort(current)) + return; + + OstProto::Port config; + config.set_transmit_mode(plm->port(current).transmitMode()); + config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); + + PortConfigDialog dialog(config, this); + + if (dialog.exec() == QDialog::Accepted) + plm->portGroup(current.parent()).modifyPort(current.row(), config); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; + QString fileName; + QString errorStr; + bool append = true; + bool ret; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + dirName = QFileInfo(fileName).absolutePath(); + +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; + QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need.On some Linux distros the native dialog can't + // distinguish between Ostinato(*) and PCAP(*) +#if defined(Q_OS_MAC) || defined(Q_OS_UNIX) + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); + + Q_ASSERT(plm->isPort(current)); + +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); + if (fileName.isEmpty()) + goto _exit; + + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..5c071f0 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_averagePacketsPerSec_editingFinished(); + void on_averageBitsPerSec_editingFinished(); + void updatePortRates(); + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + void on_actionPort_Configuration_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); + + void streamModelDataChanged(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a5d9261 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,299 @@ + + PortsWindow + + + + 0 + 0 + 710 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Avg pps + + + true + + + + + + + + + + Avg bps + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 1 + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + Port Configuration ... + + + + + + + + + radioButton + toggled(bool) + averagePacketsPerSec + setEnabled(bool) + + + 313 + 28 + + + 380 + 28 + + + + + radioButton_2 + toggled(bool) + averageBitsPerSec + setEnabled(bool) + + + 333 + 55 + + + 395 + 56 + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..0e54bbd --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,119 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "../common/ostprotolib.h" +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..78109ab --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..d64b4cb --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,226 @@ + + Preferences + + + + 0 + 0 + 400 + 220 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 'wireshark' Path + + + wiresharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + Qt::Vertical + + + + 21 + 61 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..d76bb47 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..a24c971 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,79 @@ +/* +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 +*/ + +#include +#include + +#include "stream.h" +//#include "../common/protocollist.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ +#if 0 + //protocols.loadConfigWidgets(); + foreach(AbstractProtocol* proto, *currentFrameProtocols) + { + proto->loadConfigWidget(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->loadConfigWidget(); + } + delete iter; +#endif +} + +void Stream::storeProtocolWidgets() +{ +#if 0 + //protocols.storeConfigWidgets(); + foreach(const AbstractProtocol* proto, frameProtocol()) + { + proto->storeConfigWidget(); + _iter->toFront(); + } +#else + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + p->storeConfigWidget(); + } + delete iter; +#endif +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..e2fc3f0 --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1143 @@ +/* +Copyright (C) 2010-2011 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 + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +// FIXME(HI) - remove +#include "../common/protocolmanager.h" +extern ProtocolManager *OstProtocolManager; + +QRect StreamConfigDialog::lastGeometry; +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; + +static const uint kEthFrameOverHead = 20; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setHidden(true); + pbNext->setHidden(true); + //! \todo Support Goto Stream Id + leStreamId->setHidden(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + + switch(mPort.transmitMode()) + { + case OstProto::kSequentialTransmit: + rbModeFixed->setChecked(true); + rbModeContinuous->setDisabled(true); + break; + case OstProto::kInterleavedTransmit: + rbModeContinuous->setChecked(true); + rbModeFixed->setDisabled(true); + + nextWhat->setHidden(true); + break; + default: + Q_ASSERT(false); // Unreachable + } + + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + tbProtocolData->addItem(p->configWidget(), p->name()); + } + + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); + + tbProtocolData->show(); + break; + } + + // Stream Control + case 2: + { + StoreCurrentStream(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + mpStream->loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText( + QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); + leBurstsPerSec->setText( + QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + + leGapIsg->setText("0.0"); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + pStream->storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate( + QLocale().toDouble(lePacketsPerSec->text(), &isOk)); + pStream->setBurstRate( + QLocale().toDouble(leBurstsPerSec->text(), &isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + mpStream->storeProtocolWidgets(); + mpStream->loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) +{ + if (checked) + on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); +} + +void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) +{ + if (checked) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) +{ + if (rbSendBursts->isChecked()) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); + + if (rbPacketsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("0.0")); + leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); + } +} + +void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = burstsPerSec * + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + if (rbBurstsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); + leGapIpg->setText(QString("0.0")); + } + qDebug("end of %s", __FUNCTION__); +} + +void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk)/ + double((frameLen+kEthFrameOverHead)*8); + lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); + } + else if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk)/ + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); + } +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) + && (mpStream->isFrameVariable())) + { + log += "In 'Interleaved Streams' transmit mode, the count for " + "varying fields at transmit time may not be same as configured\n"; + } + + mpStream->preflightCheck(log); + + if (log.length()) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastGeometry = geometry(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..a3214c3 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,146 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + // "Stream Control" related + void on_rbPacketsPerSec_toggled(bool checked); + void on_rbBurstsPerSec_toggled(bool checked); + + void on_lePacketsPerBurst_textChanged(const QString &text); + void on_lePacketsPerSec_textChanged(const QString &text); + void on_leBurstsPerSec_textChanged(const QString &text); + void on_leBitsPerSec_textEdited(const QString &text); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..be71e47 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1462 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumBursts + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + lePacketsPerBurst + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Bursts/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bits/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto First + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + + 20 + 41 + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + true + + + Gaps (in seconds) + + + false + + + false + + + + + + + + + :/icons/gaps.png + + + + + + + ISG + + + leGapIsg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IBG + + + leGapIbg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leGapIpg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 153 + 21 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
    dumpview.h
    + 1 +
    +
    + + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 623 + 496 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 463 + 143 + + + 463 + 177 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setEnabled(bool) + + + 30 + 68 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setEnabled(bool) + + + 30 + 95 + + + 299 + 132 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 30 + 95 + + + 134 + 189 + + + + + rbPacketsPerSec + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 299 + 82 + + + 299 + 108 + + + + + rbBurstsPerSec + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 299 + 132 + + + 299 + 158 + + + + + rbBitsPerSec + toggled(bool) + leBitsPerSec + setEnabled(bool) + + + 299 + 182 + + + 299 + 208 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setChecked(bool) + + + 95 + 70 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setChecked(bool) + + + 96 + 98 + + + 299 + 132 + + + + + rbModeContinuous + toggled(bool) + leNumPackets + setDisabled(bool) + + + 73 + 196 + + + 164 + 108 + + + + + rbModeContinuous + toggled(bool) + leNumBursts + setDisabled(bool) + + + 96 + 199 + + + 226 + 155 + + + + +
    diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..c66f02c --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,288 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + int count = StreamMaxFields; + if (mCurrentPort && + (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) + count--; + + return count; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..d559618 --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamStatus, + StreamName, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..15271d7 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ + stop_ = false; +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..1f8447d --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class AbstractFileFormat : public QThread +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif + diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..77def8f --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,928 @@ +/* +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 +*/ + +#include + +#include "abstractprotocol.h" +#include "streambase.h" +#include "protocollistiterator.h" + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + - configWidget() [pure virtual] + - loadConfigWidget() [pure virtual] + - storeConfigWidget() [pure virtual] + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + - protocolFrameVariableCount() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return (protocolFrameVariableCount() > 1); +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns the minimum number of frames required for the protocol to + vary its fields + + This is the lowest common multiple (LCM) of the counts of all the varying + fields in the protocol. Use the AbstractProtocol::lcm() static utility + function to calculate the LCM. + + The default implementation returns 1 implying that the protocol has no + varying fields. A subclass should reimplement if it has varying fields + e.g. an IP protocol that increments/decrements the IP address with + every packet +*/ +int AbstractProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +int AbstractProtocol::protocolFramePayloadVariableCount() const +{ + int count = 1; + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable() + || p->isProtocolFrameSizeVariable()) + count = lcm(count, p->protocolFrameVariableCount()); + p = p->next; + } + if (parent && (parent->isProtocolFramePayloadValueVariable() + || parent->isProtocolFramePayloadSizeVariable())) + count = lcm(count, parent->protocolFramePayloadVariableCount()); + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + \fn virtual QWidget* AbstractProtocol::configWidget() = 0; + + Returns the configuration widget for the protocol. The protocol retains + ownership of the configuration widget - the caller should not free it. + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::loadConfigWidget() = 0; + + Loads data from the protocol's protobuf into the configuration widget of + the protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +/*! + \fn virtual void AbstractProtocol::storeConfigWidget() = 0; + + Stores data from the configuration widget of the protocol into the protocol's + protobuf + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + +// Stein's binary GCD algo - from wikipedia +quint64 AbstractProtocol::gcd(quint64 u, quint64 v) +{ + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Let shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + quint64 diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +quint64 AbstractProtocol::lcm(quint64 u, quint64 v) +{ +#if 0 + /* LCM(0,x) := x */ + if (u == 0 || v == 0) + return u | v; +#else + /* For our use case, neither u nor v can ever be 0, the minimum + value is 1; we do this correction silently here */ + if (u == 0) u = 1; + if (v == 0) v = 1; + + if (u == 1 || v == 1) + return (u * v); +#endif + + return (u * v)/gcd(u, v); +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..ab3b7e9 --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,167 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + virtual QWidget* configWidget() = 0; + virtual void loadConfigWidget() = 0; + virtual void storeConfigWidget() = 0; + + static quint64 lcm(quint64 u, quint64 v); + static quint64 gcd(quint64 u, quint64 v); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..84b10a8 --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,993 @@ +/* +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 +*/ + +#include +#include + +#include "arp.h" + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; + configForm = NULL; +} + +ArpProtocol::~ArpProtocol() +{ + delete configForm; +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToHexStr(hwAddr, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + return true; + + return false; +} + +int ArpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); + } + + return count; +} + +QWidget* ArpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new ArpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void ArpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hwType->setText( + fieldData(arp_hwType, FieldValue).toString()); + configForm->protoType->setText(uintToHexStr( + fieldData(arp_protoType, FieldValue).toUInt(), 2)); + configForm->hwAddrLen->setText( + fieldData(arp_hwAddrLen, FieldValue).toString()); + configForm->protoAddrLen->setText( + fieldData(arp_protoAddrLen, FieldValue).toString()); + + configForm->opCodeCombo->setValue( + fieldData(arp_opCode, FieldValue).toUInt()); + + configForm->senderHwAddr->setText(uintToHexStr( + fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); + configForm->senderHwAddrMode->setCurrentIndex( + fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); + configForm->senderHwAddrCount->setText( + fieldData(arp_senderHwAddrCount, FieldValue).toString()); + + configForm->senderProtoAddr->setText(QHostAddress( + fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); + configForm->senderProtoAddrMode->setCurrentIndex( + fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); + configForm->senderProtoAddrCount->setText( + fieldData(arp_senderProtoAddrCount, FieldValue).toString()); + configForm->senderProtoAddrMask->setText(QHostAddress( + fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); + + configForm->targetHwAddr->setText(uintToHexStr( + fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); + configForm->targetHwAddrMode->setCurrentIndex( + fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); + configForm->targetHwAddrCount->setText( + fieldData(arp_targetHwAddrCount, FieldValue).toString()); + + configForm->targetProtoAddr->setText(QHostAddress( + fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); + configForm->targetProtoAddrMode->setCurrentIndex( + fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); + configForm->targetProtoAddrCount->setText( + fieldData(arp_targetProtoAddrCount, FieldValue).toString()); + configForm->targetProtoAddrMask->setText(QHostAddress( + fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); + +} + +void ArpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(arp_hwType, configForm->hwType->text()); + setFieldData(arp_protoType, configForm->protoType->text().toUInt( + &isOk, BASE_HEX)); + setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); + setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); + + setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); + + setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_senderHwAddrMode, + configForm->senderHwAddrMode->currentIndex()); + setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); + + setFieldData(arp_senderProtoAddr, QHostAddress( + configForm->senderProtoAddr->text()).toIPv4Address()); + setFieldData(arp_senderProtoAddrMode, + configForm->senderProtoAddrMode->currentIndex()); + setFieldData(arp_senderProtoAddrCount, + configForm->senderProtoAddrCount->text()); + setFieldData(arp_senderProtoAddrMask, QHostAddress( + configForm->senderProtoAddrMask->text()).toIPv4Address()); + + setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() + .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); + setFieldData(arp_targetHwAddrMode, + configForm->targetHwAddrMode->currentIndex()); + setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); + + setFieldData(arp_targetProtoAddr, QHostAddress( + configForm->targetProtoAddr->text()).toIPv4Address()); + setFieldData(arp_targetProtoAddrMode, + configForm->targetProtoAddrMode->currentIndex()); + setFieldData(arp_targetProtoAddrCount, + configForm->targetProtoAddrCount->text()); + setFieldData(arp_targetProtoAddrMask, QHostAddress( + configForm->targetProtoAddrMask->text()).toIPv4Address()); +} + diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..d5582d2 --- /dev/null +++ b/common/arp.h @@ -0,0 +1,121 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "arp.pb.h" +#include "ui_arp.h" + +#include "abstractprotocol.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpConfigForm : public QWidget, public Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +class ArpProtocol : public AbstractProtocol +{ +private: + OstProto::Arp data; + ArpConfigForm *configForm; + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + +public: + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
    diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..bc148d3 --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,223 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + QWidget *configForm; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + configForm = NULL; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + if (configForm) + { + protoA->configWidget()->setParent(0); + protoB->configWidget()->setParent(0); + delete configForm; + } + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + virtual int protocolFrameVariableCount() const + { + return AbstractProtocol::lcm( + protoA->protocolFrameVariableCount(), + protoB->protocolFrameVariableCount()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + + virtual QWidget* configWidget() + { + if (configForm == NULL) + { + QVBoxLayout *layout = new QVBoxLayout; + + configForm = new QWidget; + layout->addWidget(protoA->configWidget()); + layout->addWidget(protoB->configWidget()); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + configForm->setLayout(layout); + } + return configForm; + } + virtual void loadConfigWidget() + { + protoA->loadConfigWidget(); + protoB->loadConfigWidget(); + } + virtual void storeConfigWidget() + { + protoA->storeConfigWidget(); + protoB->storeConfigWidget(); + } +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..6b7afb0 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,239 @@ +/* +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 +*/ + +#include "dot3.h" +#include "streambase.h" + +#include +#include +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Dot3Protocol::~Dot3Protocol() +{ + delete configForm; +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +int Dot3Protocol::protocolFrameVariableCount() const +{ + return protocolFramePayloadVariableCount(); +} + +QWidget* Dot3Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Dot3ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Dot3Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideLength->setChecked( + fieldData(dot3_is_override_length, FieldValue).toBool()); + configForm->leLength->setText( + fieldData(dot3_length, FieldValue).toString()); +} + +void Dot3Protocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(dot3_is_override_length, + configForm->cbOverrideLength->isChecked()); + setFieldData(dot3_length,configForm->leLength->text()); +} + diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..22c3f5b --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,80 @@ +/* +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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" + +#include "dot3.pb.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : public QWidget, public Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); +}; + +class Dot3Protocol : public AbstractProtocol +{ +private: + OstProto::Dot3 data; + Dot3ConfigForm *configForm; + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + +public: + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..73038b7 --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,231 @@ +/* +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 +*/ + +#include +#include + +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Eth2Protocol::~Eth2Protocol() +{ + delete configForm; +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +QWidget* Eth2Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Eth2ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Eth2Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideType->setChecked( + fieldData(eth2_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(eth2_type, FieldValue).toUInt(), 2)); +} + +void Eth2Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(eth2_is_override_type, + configForm->cbOverrideType->isChecked()); + data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5694339 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : public QWidget, public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); +}; + +class Eth2Protocol : public AbstractProtocol +{ +private: + OstProto::Eth2 data; + Eth2ConfigForm *configForm; + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + +public: + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..4edd980 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,483 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + postParseFixup(meta.data(), content); + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void FileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..409b8a8 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include "fileformat.pb.h" + +class FileFormat : public AbstractFileFormat +{ +public: + FileFormat(); + ~FileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 2; + static const uint kFileFormatVersionRevision = 3; +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..a7b4a3d --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,1055 @@ +/* +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 +*/ + +#include "gmp.h" + +#include +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +GmpProtocol::~GmpProtocol() +{ + delete configForm; +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +int GmpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return count; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(kGroupCount, FieldValue).toUInt()); + } + + return count; +} + +void GmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); + // XXX: configForm->maxResponseTime set by subclass + configForm->overrideChecksum->setChecked( + fieldData(kIsOverrideChecksum, FieldValue).toBool()); + configForm->checksum->setText(uintToHexStr( + fieldData(kChecksum, FieldValue).toUInt(), 2)); + + configForm->groupAddress->setText( + fieldData(kGroupAddress, FieldValue).toString()); + configForm->groupMode->setCurrentIndex( + fieldData(kGroupMode, FieldValue).toUInt()); + configForm->groupCount->setText( + fieldData(kGroupCount, FieldValue).toString()); + configForm->groupPrefix->setText( + fieldData(kGroupPrefix, FieldValue).toString()); + + configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); + configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); + configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); + + QStringList sl = fieldData(kSources, FieldValue).toStringList(); + configForm->sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + configForm->sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + configForm->overrideSourceCount->setChecked( + fieldData(kIsOverrideSourceCount, FieldValue).toBool()); + configForm->sourceCount->setText( + fieldData(kSourceCount, FieldValue).toString()); + + QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); + configForm->groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(configForm->groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + configForm->groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + configForm->overrideGroupRecordCount->setChecked( + fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); + configForm->groupRecordCount->setText( + fieldData(kGroupRecordCount, FieldValue).toString()); + +} + +void GmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + configForm->update(); + + setFieldData(kType, configForm->msgTypeCombo->currentValue()); + // XXX: configForm->maxResponseTime handled by subclass + setFieldData(kIsOverrideChecksum, + configForm->overrideChecksum->isChecked()); + setFieldData(kChecksum, + configForm->checksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(kGroupAddress, configForm->groupAddress->text()); + setFieldData(kGroupMode, configForm->groupMode->currentIndex()); + setFieldData(kGroupCount, configForm->groupCount->text()); + setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); + + setFieldData(kSFlag, configForm->sFlag->isChecked()); + setFieldData(kQrv, configForm->qrv->text()); + setFieldData(kQqic, configForm->qqi->text()); + + QStringList list; + for (int i = 0; i < configForm->sourceList->count(); i++) + list.append(configForm->sourceList->item(i)->text()); + setFieldData(kSources, list); + + // sourceCount should be AFTER sources + setFieldData(kIsOverrideSourceCount, + configForm->overrideSourceCount->isChecked()); + setFieldData(kSourceCount, configForm->sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < configForm->groupList->count(); i++) + { + QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + setFieldData(kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + setFieldData(kIsOverrideGroupRecordCount, + configForm->overrideGroupRecordCount->isChecked()); + setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..7f3fd29 --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,163 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "gmp.pb.h" +#include "ui_gmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ +class GmpProtocol; + +class GmpConfigForm : public QWidget, public Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + void update(); +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +class GmpProtocol : public AbstractProtocol +{ +public: + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + OstProto::Gmp data; + GmpConfigForm *configForm; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
    diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..f579430 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,263 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +#include + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +HexDumpProtocol::~HexDumpProtocol() +{ + delete configForm; +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + +QWidget* HexDumpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new HexDumpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void HexDumpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->hexEdit->setData( + fieldData(hexDump_content, FieldValue).toByteArray()); + configForm->padUntilEnd->setChecked( + fieldData(hexDump_pad_until_end, FieldValue).toBool()); +} + +void HexDumpProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(hexDump_content, configForm->hexEdit->data()); + setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..f5b6932 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "hexdump.pb.h" +#include "ui_hexdump.h" + +#include "abstractprotocol.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpConfigForm : public QWidget, public Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +class HexDumpProtocol : public AbstractProtocol +{ +public: + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +private: + OstProto::HexDump data; + HexDumpConfigForm *configForm; + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
    qhexedit.h
    + 1 +
    +
    + + +
    diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..e7f6267 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,594 @@ +/* +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 +*/ + + +#include "icmp.h" + +#include +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +static bool isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : QWidget(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +IcmpProtocol::~IcmpProtocol() +{ + delete configForm; +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +QWidget* IcmpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new IcmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IcmpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->versionGroup->button(icmpVersion())->click(); + + configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); + configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); + + configForm->overrideCksum->setChecked( + fieldData(icmp_is_override_checksum, FieldValue).toBool()); + configForm->cksumEdit->setText(uintToHexStr( + fieldData(icmp_checksum, FieldValue).toUInt(), 2)); + + configForm->idEdit->setText( + fieldData(icmp_identifier, FieldValue).toString()); + configForm->seqEdit->setText( + fieldData(icmp_sequence, FieldValue).toString()); + +} + +void IcmpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(icmp_version, configForm->versionGroup->checkedId()); + + setFieldData(icmp_type, configForm->typeCombo->currentValue()); + setFieldData(icmp_code, configForm->codeEdit->text()); + + setFieldData(icmp_is_override_checksum, + configForm->overrideCksum->isChecked()); + setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(icmp_identifier, configForm->idEdit->text()); + setFieldData(icmp_sequence, configForm->seqEdit->text()); +} + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..a3fc296 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,117 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "icmp.pb.h" +#include "ui_icmp.h" + +#include "abstractprotocol.h" + +#include + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpConfigForm : public QWidget, public Ui::Icmp +{ + Q_OBJECT +public: + QButtonGroup *versionGroup; + + IcmpConfigForm(QWidget *parent = 0); +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +class IcmpProtocol : public AbstractProtocol +{ +private: + OstProto::Icmp data; + IcmpConfigForm *configForm; + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } + +public: + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
    diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..046f675 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,449 @@ +/* +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 +*/ + +#include "igmp.h" + +#include "ipv4addressdelegate.h" +#include "iputils.h" + +#include +#include + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* IgmpProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new IgmpConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void IgmpProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kRsvdMrtCode, FieldValue).toString()); +} + +void IgmpProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..6ee9ad3 --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,111 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "igmp.pb.h" +#include "gmp.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..a7d6ef7 --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,752 @@ +/* +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 +*/ + +#include +#include + +#include "ip4.h" + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ + qDebug("IPv4 Config Form destructor called"); +} + +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +Ip4Protocol::~Ip4Protocol() +{ + delete configForm; +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + // Meta fields + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + } + default: + break; + } + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +int Ip4Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.src_ip_count()); + + if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.dst_ip_count()); + + return count; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip4Protocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new Ip4ConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void Ip4Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); + configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); + + configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); + configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); + + configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); + + configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); + configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); + + configForm->leIpId->setText(uintToHexStr(data.id(), 2)); + configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); + configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); + configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); + + configForm->leIpTtl->setText(QString().setNum(data.ttl())); + + configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); + configForm->leIpProto->setText(uintToHexStr( + fieldData(ip4_proto, FieldValue).toUInt(), 1)); + + configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); + configForm->leIpCksum->setText(uintToHexStr( + fieldData(ip4_cksum, FieldValue).toUInt(), 2)); + + configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); + configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); + configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); + configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); + + configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); + configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); + configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); + configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); +} + +void Ip4Protocol::storeConfigWidget() +{ + uint ff = 0; + bool isOk; + + configWidget(); + + data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); + data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | + (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); + data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); + + data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); + + data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); + data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); + + data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); + data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); + + if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + data.set_flags(ff); + + data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); + + data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); + data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); + data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk, 16)); + + data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); + data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); + data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); + data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); + + data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); + data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); + data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); + data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); +} + diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..6f89b09 --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,116 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" + +#include "ip4.pb.h" +#include "ui_ip4.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + + +class Ip4ConfigForm : public QWidget, public Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + ~Ip4ConfigForm(); +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; + +class Ip4Protocol : public AbstractProtocol +{ +private: + OstProto::Ip4 data; + Ip4ConfigForm *configForm; + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + +public: + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..266c8c2 --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,953 @@ +/* +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 +*/ + +#include "ip6.h" + +#include "ipv6addressvalidator.h" + +#include +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +Ip6Protocol::~Ip6Protocol() +{ + delete configForm; +} + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +int Ip6Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + 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) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* Ip6Protocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new Ip6ConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void Ip6Protocol::loadConfigWidget() +{ + configWidget(); + + configForm->isVersionOverride->setChecked( + fieldData(ip6_isOverrideVersion, FieldValue).toBool()); + configForm->version->setText( + fieldData(ip6_version, FieldValue).toString()); + + configForm->trafficClass->setText(uintToHexStr( + fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); + + configForm->flowLabel->setText(QString("%1").arg( + fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); + + configForm->isPayloadLengthOverride->setChecked( + fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); + configForm->payloadLength->setText( + fieldData(ip6_payloadLength, FieldValue).toString()); + + configForm->isNextHeaderOverride->setChecked( + fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); + configForm->nextHeader->setText(uintToHexStr( + fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); + + configForm->hopLimit->setText( + fieldData(ip6_hopLimit, FieldValue).toString()); + + configForm->srcAddr->setText( + fieldData(ip6_srcAddress, FieldTextValue).toString()); + configForm->srcAddrModeCombo->setCurrentIndex( + fieldData(ip6_srcAddrMode, FieldValue).toUInt()); + configForm->srcAddrCount->setText( + fieldData(ip6_srcAddrCount, FieldValue).toString()); + configForm->srcAddrPrefix->setText( + fieldData(ip6_srcAddrPrefix, FieldValue).toString()); + + configForm->dstAddr->setText( + fieldData(ip6_dstAddress, FieldTextValue).toString()); + configForm->dstAddrModeCombo->setCurrentIndex( + fieldData(ip6_dstAddrMode, FieldValue).toUInt()); + configForm->dstAddrCount->setText( + fieldData(ip6_dstAddrCount, FieldValue).toString()); + configForm->dstAddrPrefix->setText( + fieldData(ip6_dstAddrPrefix, FieldValue).toString()); +} + +void Ip6Protocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(ip6_isOverrideVersion, + configForm->isVersionOverride->isChecked()); + setFieldData(ip6_version, configForm->version->text()); + + setFieldData(ip6_trafficClass, + configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_flowLabel, + configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_isOverridePayloadLength, + configForm->isPayloadLengthOverride->isChecked()); + setFieldData(ip6_payloadLength, configForm->payloadLength->text()); + + setFieldData(ip6_isOverrideNextHeader, + configForm->isNextHeaderOverride->isChecked()); + setFieldData(ip6_nextHeader, + configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + setFieldData(ip6_hopLimit, configForm->hopLimit->text()); + + setFieldData(ip6_srcAddress, configForm->srcAddr->text()); + setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); + setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); + setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); + + setFieldData(ip6_dstAddress, configForm->dstAddr->text()); + setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); + setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); + setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..dfaf02d --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,131 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "ip6.pb.h" +#include "ui_ip6.h" + +#include "abstractprotocol.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6ConfigForm : public QWidget, public Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +class Ip6Protocol : public AbstractProtocol +{ +private: + OstProto::Ip6 data; + Ip6ConfigForm *configForm; + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + +public: + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..5adecf1 --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,331 @@ +/* +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 +*/ + +#include +#include + +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +LlcProtocol::~LlcProtocol() +{ + delete configForm; +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + + +QWidget* LlcProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new LlcConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void LlcProtocol::loadConfigWidget() +{ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + + configWidget(); + + configForm->cbOverrideDsap->setChecked( + fieldData(llc_is_override_dsap, FieldValue).toBool()); + configForm->leDsap->setText(uintToHexStr( + fieldData(llc_dsap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideSsap->setChecked( + fieldData(llc_is_override_ssap, FieldValue).toBool()); + configForm->leSsap->setText(uintToHexStr( + fieldData(llc_ssap, FieldValue).toUInt(), 1)); + + configForm->cbOverrideControl->setChecked( + fieldData(llc_is_override_ctl, FieldValue).toBool()); + configForm->leControl->setText(uintToHexStr( + fieldData(llc_ctl, FieldValue).toUInt(), 1)); +#undef uintToHexStr +} + +void LlcProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(llc_is_override_dsap, + configForm->cbOverrideDsap->isChecked()); + setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ssap, + configForm->cbOverrideSsap->isChecked()); + setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(llc_is_override_ctl, + configForm->cbOverrideControl->isChecked()); + setFieldData(llc_ctl, + configForm->leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..808d442 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include +#include + +#include "abstractprotocol.h" + +#include "llc.pb.h" +#include "ui_llc.h" + +class LlcConfigForm : public QWidget, public Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); +}; + +class LlcProtocol : public AbstractProtocol +{ +private: + OstProto::Llc data; + LlcConfigForm *configForm; + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + +public: + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..788a10d --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,329 @@ +/* +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 +*/ + +#include +#include + +#include "mac.h" + +MacConfigForm::MacConfigForm(QWidget *parent) + : QWidget(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ + qDebug("In MacConfigForm destructor"); +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +MacProtocol::~MacProtocol() +{ + delete configForm; +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToHexStr(dstMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToHexStr(srcMac, 6); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + // Meta fields + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + +int MacProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.dst_mac_count()); + + if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.src_mac_count()); + + return count; +} + +QWidget* MacProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new MacConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void MacProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); + configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); + configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); + configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); + + configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); + configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); + configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); + configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); +} + +void MacProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbDstMacMode->currentIndex()); + data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); + data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); + + data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). + toULongLong(&isOk, 16)); + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> + cmbSrcMacMode->currentIndex()); + data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); + data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..32cd7e2 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" +#include "ui_mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +class MacConfigForm : public QWidget, public Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +class MacProtocol : public AbstractProtocol +{ +private: + OstProto::Mac data; + MacConfigForm *configForm; + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + +public: + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..4c373ae --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,624 @@ +/* +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 +*/ + +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" +#include "iputils.h" + +#include +#include + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j+8)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +QWidget* MldProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new MldConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void MldProtocol::loadConfigWidget() +{ + GmpProtocol::loadConfigWidget(); + + configForm->maxResponseTime->setText( + fieldData(kMldMrt, FieldValue).toString()); +} + +void MldProtocol::storeConfigWidget() +{ + GmpProtocol::storeConfigWidget(); + + setFieldData(kMldMrt, configForm->maxResponseTime->text()); +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..bc2d95e --- /dev/null +++ b/common/mld.h @@ -0,0 +1,108 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "mld.pb.h" +#include "gmp.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..f0563fc --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,142 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network script xml +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf +FORMS += \ + pcapfileimport.ui \ + mac.ui \ + payload.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + hexdump.ui \ + sample.ui +PROTOS += \ + protocol.proto \ + fileformat.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto +HEADERS += \ + ostprotolib.h \ + abstractprotocol.h \ + comboprotocol.h \ + abstractfileformat.h \ + fileformat.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + mac.h \ + payload.h \ + eth2.h \ + dot3.h \ + llc.h \ + snap.h \ + dot2llc.h \ + dot2snap.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + arp.h \ + ip4.h \ + ip6.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + ip6over4.h \ + ip4over6.h \ + ip4over4.h \ + ip6over6.h \ + icmp.h \ + gmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + userscript.h \ + hexdump.h \ + sample.h +SOURCES += \ + ostprotolib.cpp \ + abstractprotocol.cpp \ + crc32c.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + mac.cpp \ + payload.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + vlan.cpp \ + svlan.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + icmp.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + userscript.cpp \ + hexdump.cpp \ + sample.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..e46dc8c --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} + diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..7fd0dd5 --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,284 @@ +/* +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 +*/ + +#include +#include + +//#include "../client/stream.h" +#include "payload.h" +#include "streambase.h" + + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +PayloadProtocol::~PayloadProtocol() +{ + delete configForm; +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + +int PayloadProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.pattern_mode() == OstProto::Payload::e_dp_random) + { + switch(mpStream->sendUnit()) + { + case OstProto::StreamControl::e_su_packets: + return mpStream->numPackets(); + + case OstProto::StreamControl::e_su_bursts: + return int(mpStream->numBursts() + * mpStream->burstSize() + * mpStream->burstRate()); + } + } + + if (mpStream->lenMode() != StreamBase::e_fl_fixed) + { + count = AbstractProtocol::lcm(count, + mpStream->frameLenMax() - mpStream->frameLenMin() + 1); + } + + return count; +} + +QWidget* PayloadProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new PayloadConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void PayloadProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); + configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); +} + +void PayloadProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_pattern_mode((OstProto::Payload::DataPatternMode) + configForm->cmbPatternMode->currentIndex()); + data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); +} + diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..822ee37 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,85 @@ +/* +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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" +#include "ui_payload.h" + +class PayloadConfigForm : public QWidget, public Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +class PayloadProtocol : public AbstractProtocol +{ +private: + OstProto::Payload data; + PayloadConfigForm *configForm; + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + +public: + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..bc0ccd6 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,661 @@ +/* +Copyright (C) 2011 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 "pcapfileformat.h" + +#include "pdmlreader.h" +#include "ostprotolib.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; +const quint32 kDltEthernet = 1; + +PcapFileFormat pcapFileFormat; + +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + +PcapFileFormat::PcapFileFormat() +{ + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + + importDialog_ = NULL; +} + +PcapFileFormat::~PcapFileFormat() +{ + delete importDialog_; +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QTemporaryFile file2; + quint32 magic; + uchar gzipMagic[2]; + int len; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; + + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; + + emit status("Decompressing..."); + emit target(0); + + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + gzip.start(OstProtoLib::gzipPath(), + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + + fd_ >> magic; + + qDebug("magic = %08x", magic); + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); + else + fd_.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif + + pktBuf.resize(fileHdr.snapLen); + + if (importOptions_.value("ViaPdml").toBool()) + { + QProcess tshark; + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + diff.start(OstProtoLib::diffPath(), + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff. Check path in Preferences.\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + + goto _exit; + } + +_non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage + pktCount = 1; + while (!fd_.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + readPacket(pktHdr, pktBuf); + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; + pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; + } + + isOk = true; + goto _exit; + +_user_cancel: + isOk = true; + goto _exit; + +_diff_fail: + goto _exit; + +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +#if 0 +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + file.close(); + return isOk; +} + +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd_.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = kDltEthernet; + + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + emit status("Writing Packets..."); + emit target(streams.stream_size()); + + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += 1000000/s.packetRate(); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } + + emit progress(i); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..064aaf1 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "abstractfileformat.h" +#include "ui_pcapfileimport.h" + +#include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; + +class PdmlReader; +class PcapFileFormat : public AbstractFileFormat +{ + friend class PdmlReader; + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + virtual QDialog* openOptionsDialog(); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..9ba1f2d --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,163 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" + +#include "ostprotolib.h" +#include "pdmlreader.h" + +#include +#include + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } + + goto _exit; + +_open_fail: + isOk = false; + +_user_cancel: +_exit: + delete reader; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; +} + +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + xml.readNext(); + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..e05026a --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "abstractfileformat.h" + +class PdmlFileFormat : public AbstractFileFormat +{ +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..cb39a4f --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,153 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +const int kBaseHex = 16; + +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +PdmlProtocol::~PdmlProtocol() +{ +} + +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..412f588 --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,64 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +class PdmlProtocol +{ +public: + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlProtocol(); + + int ostProtoId_; + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp new file mode 100644 index 0000000..31e1303 --- /dev/null +++ b/common/pdmlprotocols.cpp @@ -0,0 +1,1357 @@ +/* +Copyright (C) 2011 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 "pdmlprotocols.h" + +#include "arp.pb.h" +#include "eth2.pb.h" +#include "dot3.pb.h" +#include "gmp.pb.h" +#include "hexdump.pb.h" +#include "llc.pb.h" +#include "mac.pb.h" +#include "icmp.pb.h" +#include "igmp.pb.h" +#include "ip4.pb.h" +#include "ip6.pb.h" +#include "mld.pb.h" +#include "sample.pb.h" +#include "snap.pb.h" +#include "svlan.pb.h" +#include "tcp.pb.h" +#include "textproto.pb.h" +#include "udp.pb.h" +#include "vlan.pb.h" + +#include +#include + +const int kBaseHex = 16; + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +PdmlProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + endPos_ = pos; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + // Skipped field(s) at end? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), + pos, expPos_, endPos_); + + // Skipped field? Pad with zero! + if ((pos > expPos_) && (expPos_ < endPos_)) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ +} + +PdmlProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ +} + +PdmlProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} + + +// ---------------------------------------------------------- // +// PdmlSvlanProtocol // +// ---------------------------------------------------------- // + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlVlanProtocol // +// ---------------------------------------------------------- // + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + + +// ---------------------------------------------------------- // +// PdmlEthProtocol // +// ---------------------------------------------------------- // + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + + +// ---------------------------------------------------------- // +// PdmlLlcProtocol // +// ---------------------------------------------------------- // + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + + +// ---------------------------------------------------------- // +// PdmlArpProtocol // +// ---------------------------------------------------------- // + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + + +// ---------------------------------------------------------- // +// PdmlIp4Protocol // +// ---------------------------------------------------------- // + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlIp6Protocol // +// ---------------------------------------------------------- // + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + + +// ---------------------------------------------------------- // +// PdmlIcmpProtocol // +// ---------------------------------------------------------- // + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + +// ---------------------------------------------------------- // +// PdmlIcmp6Protocol // +// ---------------------------------------------------------- // + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + + +// ---------------------------------------------------------- // +// PdmlIgmpProtocol // +// ---------------------------------------------------------- // + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + + +// ---------------------------------------------------------- // +// PdmlMldProtocol // +// ---------------------------------------------------------- // + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + + +// ---------------------------------------------------------- // +// PdmlTcpProtocol // +// ---------------------------------------------------------- // + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + +// ---------------------------------------------------------- // +// PdmlUdpProtocol // +// ---------------------------------------------------------- // + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + + +// ---------------------------------------------------------- // +// PdmlTextProtocol // +// ---------------------------------------------------------- // + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h new file mode 100644 index 0000000..95a75c0 --- /dev/null +++ b/common/pdmlprotocols.h @@ -0,0 +1,313 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H + +#include "pdmlprotocol.h" + +class PdmlUnknownProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlGenInfoProtocol(); + +}; + +class PdmlFrameProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); +}; + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..c4ea3f0 --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,533 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdmlprotocols.h" + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + QStringList creator; + + Q_ASSERT(isStartElement() && name() == "pdml"); + + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..7de3918 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + bool isMldSupport_; + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..9a74654 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,249 @@ +/* +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 +*/ + +package OstProto; +option cc_generic_services = true; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; + optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; + optional double packets_per_sec = 9 [default = 1]; + optional double bursts_per_sec = 10 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +enum TransmitMode { + kSequentialTransmit = 0; + kInterleavedTransmit = 1; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; + optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; + + optional uint64 rx_drops = 100; + optional uint64 rx_errors = 101; + optional uint64 rx_fifo_errors = 102; + optional uint64 rx_frame_errors = 103; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..e91f270 --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,212 @@ +/* +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 +*/ + +#include "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "dot3.h" +#include "llc.h" +#include "snap.h" +#include "dot2llc.h" +#include "dot2snap.h" +#include "vlan.h" +#include "vlanstack.h" +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip6over4.h" +#include "ip4over6.h" +#include "ip4over4.h" +#include "ip6over6.h" +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" +#include "textproto.h" +#include "userscript.h" +#include "hexdump.h" +#include "sample.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT(pc != NULL); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..07b0604 --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,57 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..42f9f32 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,546 @@ +/* +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 +*/ + +#include + +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +SampleProtocol::~SampleProtocol() +{ + delete configForm; +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 1234; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return 0; +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (a << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | b); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame has any variable fields or has a variable + size, return the minimum number of frames required to vary the fields \n + + Otherwise you don't need to reimplement this method - the base class always + returns 1 +*/ +int SampleProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +QWidget* SampleProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new SampleConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocol::loadConfigWidget() for more info +*/ +void SampleProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); + configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); + + configForm->samplePayloadLength->setText( + fieldData(sample_payloadLength, FieldValue).toString()); + + configForm->isChecksumOverride->setChecked( + fieldData(sample_is_override_checksum, FieldValue).toBool()); + configForm->sampleChecksum->setText(uintToHexStr( + fieldData(sample_checksum, FieldValue).toUInt(), 2)); + + configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); + configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); + +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocol::storeConfigWidget() for more info +*/ +void SampleProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + setFieldData(sample_a, configForm->sampleA->text()); + setFieldData(sample_b, configForm->sampleB->text()); + + setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); + setFieldData(sample_is_override_checksum, + configForm->isChecksumOverride->isChecked()); + setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); + + setFieldData(sample_x, configForm->sampleX->text()); + setFieldData(sample_y, configForm->sampleY->text()); +} + diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..90b9be8 --- /dev/null +++ b/common/sample.h @@ -0,0 +1,103 @@ +/* +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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "sample.pb.h" +#include "ui_sample.h" + +#include "abstractprotocol.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleConfigForm : public QWidget, public Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); +private slots: +}; + +class SampleProtocol : public AbstractProtocol +{ +private: + OstProto::Sample data; + SampleConfigForm *configForm; + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + +public: + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..bdb80c3 --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,307 @@ +/* +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 +*/ + +#include +#include + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +SnapProtocol::~SnapProtocol() +{ + delete configForm; +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} + +QWidget* SnapProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new SnapConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void SnapProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbOverrideOui->setChecked( + fieldData(snap_is_override_oui, FieldValue).toBool()); + configForm->leOui->setText(uintToHexStr( + fieldData(snap_oui, FieldValue).toUInt(), 3)); + + configForm->cbOverrideType->setChecked( + fieldData(snap_is_override_type, FieldValue).toBool()); + configForm->leType->setText(uintToHexStr( + fieldData(snap_type, FieldValue).toUInt(), 2)); +} + +void SnapProtocol::storeConfigWidget() +{ + bool isOk; + configWidget(); + + setFieldData(snap_is_override_oui, + configForm->cbOverrideOui->isChecked()); + setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + + setFieldData(snap_is_override_type, + configForm->cbOverrideType->isChecked()); + setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..daba802 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" +#include "ui_snap.h" + +class SnapConfigForm : public QWidget, public Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); +}; + +class SnapProtocol : public AbstractProtocol +{ +private: + OstProto::Snap data; + SnapConfigForm *configForm; + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + +public: + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..ab43117 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,565 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +/*! Convenience Function */ +quint16 StreamBase::frameLenAvg() const +{ + quint16 avgFrameLen; + + if (lenMode() == e_fl_fixed) + avgFrameLen = frameLen(); + else + avgFrameLen = (frameLenMin() + frameLenMax())/2; + + return avgFrameLen; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +double StreamBase::packetRate() const +{ + return (double) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(double packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +double StreamBase::burstRate() const +{ + return (double) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(double burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +/*! Convenience Function */ +double StreamBase::averagePacketRate() const +{ + double avgPacketRate; + + switch (sendUnit()) + { + case e_su_bursts: + avgPacketRate = burstRate() * burstSize(); + break; + case e_su_packets: + avgPacketRate = packetRate(); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return avgPacketRate; +} + +/*! Convenience Function */ +bool StreamBase::setAveragePacketRate(double packetsPerSec) +{ + switch (sendUnit()) + { + case e_su_bursts: + setBurstRate(packetsPerSec/double(burstSize())); + break; + case e_su_packets: + setPacketRate(packetsPerSec); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +int StreamBase::frameVariableCount() const +{ + ProtocolListIterator *iter; + quint64 frameCount = 1; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + int count; + + proto = iter->next(); + count = proto->protocolFrameVariableCount(); + + // correct count for mis-behaving protocols + if (count <= 0) + count = 1; + + frameCount = AbstractProtocol::lcm(frameCount, count); + } + delete iter; + + return frameCount; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..9ef3ba1 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + quint16 frameLenAvg() const; + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + double packetRate() const; + bool setPacketRate(double packetsPerSec); + + double burstRate() const; + bool setBurstRate(double burstsPerSec); + + double averagePacketRate() const; + bool setAveragePacketRate(double packetsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameVariableCount() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..893671d --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,68 @@ +/* +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 +*/ + +#include + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..39c71bf --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,709 @@ +/* +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 +*/ + +#include +#include + +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +TcpProtocol::~TcpProtocol() +{ + delete configForm; +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int TcpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + +QWidget* TcpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new TcpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void TcpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leTcpSrcPort->setText( + fieldData(tcp_src_port, FieldValue).toString()); + configForm->cbTcpSrcPortOverride->setChecked( + fieldData(tcp_is_override_src_port, FieldValue).toBool()); + + configForm->leTcpDstPort->setText( + fieldData(tcp_dst_port, FieldValue).toString()); + configForm->cbTcpDstPortOverride->setChecked( + fieldData(tcp_is_override_dst_port, FieldValue).toBool()); + + configForm->leTcpSeqNum->setText( + fieldData(tcp_seq_num, FieldValue).toString()); + configForm->leTcpAckNum->setText( + fieldData(tcp_ack_num, FieldValue).toString()); + + configForm->leTcpHdrLen->setText( + fieldData(tcp_hdrlen, FieldValue).toString()); + configForm->cbTcpHdrLenOverride->setChecked( + fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); + + configForm->leTcpWindow->setText( + fieldData(tcp_window, FieldValue).toString()); + + configForm->leTcpCksum->setText(QString("%1").arg( + fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbTcpCksumOverride->setChecked( + fieldData(tcp_is_override_cksum, FieldValue).toBool()); + + configForm->leTcpUrgentPointer->setText( + fieldData(tcp_urg_ptr, FieldValue).toString()); + + uint flags = fieldData(tcp_flags, FieldValue).toUInt(); + configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpProtocol::storeConfigWidget() +{ + bool isOk; + int ff = 0; + + configWidget(); + + setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); + setFieldData(tcp_is_override_src_port, + configForm->cbTcpSrcPortOverride->isChecked()); + setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); + setFieldData(tcp_is_override_dst_port, + configForm->cbTcpDstPortOverride->isChecked()); + + setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); + setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); + + setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); + setFieldData(tcp_is_override_hdrlen, + configForm->cbTcpHdrLenOverride->isChecked()); + + setFieldData(tcp_window, configForm->leTcpWindow->text()); + + setFieldData(tcp_cksum, configForm->leTcpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(tcp_is_override_cksum, + configForm->cbTcpCksumOverride->isChecked()); + + setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); + + if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + setFieldData(tcp_flags, ff); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..9addef0 --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,101 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" +#include "ui_tcp.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpConfigForm : public QWidget, public Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); +}; + +class TcpProtocol : public AbstractProtocol +{ +private: + OstProto::Tcp data; + TcpConfigForm *configForm; + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + +public: + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..9834530 --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,295 @@ +/* +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 +*/ + +#include + +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + /* The configWidget is created lazily */ + configForm = NULL; +} + +TextProtocol::~TextProtocol() +{ + delete configForm; +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} + +QWidget* TextProtocol::configWidget() +{ + /* Lazy creation of the configWidget */ + if (configForm == NULL) + { + configForm = new TextProtocolConfigForm; + loadConfigWidget(); + } + + return configForm; +} + +void TextProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->portNumCombo->setValue( + fieldData(textProto_portNum, FieldValue).toUInt()); + configForm->eolCombo->setCurrentIndex( + fieldData(textProto_eol, FieldValue).toUInt()); + configForm->encodingCombo->setCurrentIndex( + fieldData(textProto_encoding, FieldValue).toUInt()); + configForm->protoText->setText( + fieldData(textProto_text, FieldValue).toString()); +} + +void TextProtocol::storeConfigWidget() +{ + configWidget(); + + setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); + setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); + setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); + + setFieldData(textProto_text, configForm->protoText->toPlainText()); +} + diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..1ec5fc0 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "textproto.pb.h" +#include "ui_textproto.h" + +#include "abstractprotocol.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); +private slots: +}; + +class TextProtocol : public AbstractProtocol +{ +private: + OstProto::TextProtocol data; + TextProtocolConfigForm *configForm; + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + +public: + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..f6996aa --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProtocol + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + +
    diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..5a4e14b --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,500 @@ +/* +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 +*/ + +#include +#include + +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +UdpProtocol::~UdpProtocol() +{ + delete configForm; +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int UdpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + +QWidget* UdpProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UdpConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void UdpProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->leUdpSrcPort->setText( + fieldData(udp_srcPort, FieldValue).toString()); + configForm->cbUdpSrcPortOverride->setChecked( + fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); + configForm->leUdpDstPort->setText( + fieldData(udp_dstPort, FieldValue).toString()); + configForm->cbUdpDstPortOverride->setChecked( + fieldData(udp_isOverrideDstPort, FieldValue).toBool()); + + configForm->leUdpLength->setText( + fieldData(udp_totLen, FieldValue).toString()); + configForm->cbUdpLengthOverride->setChecked( + fieldData(udp_isOverrideTotLen, FieldValue).toBool()); + + configForm->leUdpCksum->setText(QString("%1").arg( + fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); + configForm->cbUdpCksumOverride->setChecked( + fieldData(udp_isOverrideCksum, FieldValue).toBool()); +} + +void UdpProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); + setFieldData(udp_isOverrideSrcPort, + configForm->cbUdpSrcPortOverride->isChecked()); + setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); + setFieldData(udp_isOverrideDstPort, + configForm->cbUdpDstPortOverride->isChecked()); + + setFieldData(udp_totLen, configForm->leUdpLength->text()); + setFieldData(udp_isOverrideTotLen, + configForm->cbUdpLengthOverride->isChecked()); + + setFieldData(udp_cksum, configForm->leUdpCksum->text().remove(QChar(' ')) + .toUInt(&isOk, BASE_HEX)); + setFieldData(udp_isOverrideCksum, + configForm->cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..7bdf200 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,88 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" + +#include "udp.pb.h" +#include "ui_udp.h" + +class UdpConfigForm : public QWidget, public Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); +}; + +class UdpProtocol : public AbstractProtocol +{ +private: + OstProto::Udp data; + UdpConfigForm *configForm; + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + +public: + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..fece963 --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,630 @@ +/* +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 +*/ + +#include "userscript.h" + +#include + +// +// -------------------- UserScriptConfigForm -------------------- +// + +UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, + QWidget *parent) : QWidget(parent), protocol_(protocol) +{ + setupUi(this); + updateStatus(); +} + +void UserScriptConfigForm::updateStatus() +{ + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + protocol_->storeConfigWidget(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } + updateStatus(); +} + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + configForm = NULL; + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ + delete configForm; +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +int UserScriptProtocol::protocolFrameVariableCount() const +{ + return userProtocol_.protocolFrameVariableCount(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +QWidget* UserScriptProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new UserScriptConfigForm(this); + loadConfigWidget(); + } + + return configForm; +} + +void UserScriptProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->programEdit->setPlainText( + fieldData(userScript_program, FieldValue).toString()); +} + +void UserScriptProtocol::storeConfigWidget() +{ + configWidget(); + setFieldData(userScript_program, configForm->programEdit->toPlainText()); + evaluateUserScript(); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; + protocolFrameVariableCount_ = 1; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +int UserProtocol::protocolFrameVariableCount() const +{ + return protocolFrameVariableCount_; +} + +void UserProtocol::setProtocolFrameVariableCount(int count) +{ + protocolFrameVariableCount_ = count; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +int UserProtocol::protocolFramePayloadVariableCount() const +{ + return parent_->protocolFramePayloadVariableCount(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..99ac226 --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,190 @@ +/* +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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" +#include "ui_userscript.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + Q_PROPERTY(int protocolFrameVariableCount + READ protocolFrameVariableCount + WRITE setProtocolFrameVariableCount); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp, + ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + int protocolFrameVariableCount() const; + void setProtocolFrameVariableCount(int count); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; + int protocolFrameVariableCount_; +}; + + + +class UserScriptConfigForm : public QWidget, public Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); + +private: + void updateStatus(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; + + + +class UserScriptProtocol : public AbstractProtocol +{ + +public: + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + OstProto::UserScript data; + UserScriptConfigForm *configForm; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..95b2304 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,257 @@ +/* +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 +*/ + +#include + +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); +} + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + configForm = NULL; +} + +VlanProtocol::~VlanProtocol() +{ + delete configForm; +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + + +QWidget* VlanProtocol::configWidget() +{ + if (configForm == NULL) + { + configForm = new VlanConfigForm; + loadConfigWidget(); + } + return configForm; +} + +void VlanProtocol::loadConfigWidget() +{ + configWidget(); + + configForm->cbTpidOverride->setChecked(data.is_override_tpid()); + configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); + configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); + configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); + configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); +} + +void VlanProtocol::storeConfigWidget() +{ + bool isOk; + + configWidget(); + + data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); + data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); + data.set_vlan_tag( + ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | + ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | + (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); +} + diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..728572b --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,82 @@ +/* +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 +*/ + +#ifndef _Vlan_H +#define _Vlan_H + +#include "abstractprotocol.h" + +#include "vlan.pb.h" +#include "ui_vlan.h" + +class VlanConfigForm : public QWidget, public Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); +}; + +class VlanProtocol : public AbstractProtocol +{ +private: + VlanConfigForm *configForm; + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + +protected: + OstProto::Vlan data; + +public: + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual QWidget* configWidget(); + virtual void loadConfigWidget(); + virtual void storeConfigWidget(); +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..3e0326d --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,179 @@ + + Vlan + + + + 0 + 0 + 268 + 96 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + VLAN + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..89479e7 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/commands.h\ + src/qhexedit.h \ + src/qhexedit_p.h \ + src/xbytearray.h + +SOURCES = src/commands.cpp \ + src/qhexedit.cpp \ + src/qhexedit_p.cpp \ + src/xbytearray.cpp diff --git a/extra/qhexedit2/src/commands.cpp b/extra/qhexedit2/src/commands.cpp new file mode 100644 index 0000000..303091d --- /dev/null +++ b/extra/qhexedit2/src/commands.cpp @@ -0,0 +1,115 @@ +#include "commands.h" + +CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _xData = xData; + _charPos = charPos; + _newChar = newChar; + _cmd = cmd; +} + +bool CharCommand::mergeWith(const QUndoCommand *command) +{ + const CharCommand *nextCommand = static_cast(command); + bool result = false; + + if (_cmd != remove) + { + if (nextCommand->_cmd == replace) + if (nextCommand->_charPos == _charPos) + { + _newChar = nextCommand->_newChar; + result = true; + } + } + return result; +} + +void CharCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_charPos, 1); + break; + case replace: + _xData->replace(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + case remove: + _xData->insert(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + } +} + +void CharCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_charPos, _newChar); + break; + case replace: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->replace(_charPos, _newChar); + break; + case remove: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->remove(_charPos, 1); + break; + } +} + + + +ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _cmd = cmd; + _xData = xData; + _baPos = baPos; + _newBa = newBa; + _len = len; +} + +void ArrayCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_baPos, _newBa.length()); + break; + case replace: + _xData->replace(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + case remove: + _xData->insert(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + } +} + +void ArrayCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_baPos, _newBa); + break; + case replace: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->replace(_baPos, _newBa); + break; + case remove: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->remove(_baPos, _len); + break; + } +} diff --git a/extra/qhexedit2/src/commands.h b/extra/qhexedit2/src/commands.h new file mode 100644 index 0000000..9931b3f --- /dev/null +++ b/extra/qhexedit2/src/commands.h @@ -0,0 +1,70 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +/** \cond docNever */ + +#include + +#include "xbytearray.h" + +/*! CharCommand is a class to prived undo/redo functionality in QHexEdit. +A QUndoCommand represents a single editing action on a document. CharCommand +is responsable for manipulations on single chars. It can insert. replace and +remove characters. A manipulation stores allways to actions +1. redo (or do) action +2. undo action. + +CharCommand also supports command compression via mergeWidht(). This allows +the user to execute a undo command contation e.g. 3 steps in a single command. +If you for example insert a new byt "34" this means for the editor doing 3 +steps: insert a "00", replace it with "03" and the replace it with "34". These +3 steps are combined into a single step, insert a "34". +*/ +class CharCommand : public QUndoCommand +{ +public: + enum { Id = 1234 }; + enum Cmd {insert, remove, replace}; + + CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, + QUndoCommand *parent=0); + + void undo(); + void redo(); + bool mergeWith(const QUndoCommand *command); + int id() const { return Id; } + +private: + XByteArray * _xData; + int _charPos; + bool _wasChanged; + char _newChar; + char _oldChar; + Cmd _cmd; +}; + +/*! ArrayCommand provides undo/redo functionality for handling binary strings. It +can undo/redo insert, replace and remove binary strins (QByteArrays). +*/ +class ArrayCommand : public QUndoCommand +{ +public: + enum Cmd {insert, remove, replace}; + ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, + QUndoCommand *parent=0); + void undo(); + void redo(); + +private: + Cmd _cmd; + XByteArray * _xData; + int _baPos; + int _len; + QByteArray _wasChanged; + QByteArray _newBa; + QByteArray _oldBa; +}; + +/** \endcond docNever */ + +#endif // COMMANDS_H diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..b6dd38d --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,152 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); + connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); + setFocusPolicy(Qt::NoFocus); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +QString QHexEdit::toReadableString() +{ + return qHexEdit_p->toRedableString(); +} + +QString QHexEdit::selectionToReadableString() +{ + return qHexEdit_p->selectionToReadableString(); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::redo() +{ + qHexEdit_p->redo(); +} + +void QHexEdit::undo() +{ + qHexEdit_p->undo(); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return qHexEdit_p->addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setSelectionColor(const QColor &color) +{ + qHexEdit_p->setSelectionColor(color); +} + +QColor QHexEdit::selectionColor() +{ + return qHexEdit_p->selectionColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setReadOnly(bool readOnly) +{ + qHexEdit_p->setReadOnly(readOnly); +} + +bool QHexEdit::isReadOnly() +{ + return qHexEdit_p->isReadOnly(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + +const QFont & QHexEdit::font() const +{ + return qHexEdit_p->font(); +} diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..b5a9601 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,205 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.6.1 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text +data. There are sip configuration files included, so it is easy to create +bindings for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use +the mouse or the keyboard to navigate inside the widget. If you hit the keys +(0..9, a..f) you will change the data. Changed data is highlighted and can be +accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) +and insert data. In this case the size of data() increases. It is also possible +to delete bytes (del or backspace), here the size of data decreases. + +You can select data with keyboard hits or mouse movements. The copy-key will +copy the selected data into the clipboard. The cut-key copies also but delets +it afterwards. In overwrite mode, the paste function overwrites the content of +the (does not change the length) data. In insert mode, clipboard data will be +inserted. The clipboard content is expected in ASCII Hex notation. Unknown +characters will be ignored. + +QHexEdit comes with undo/redo functionality. All changes can be undone, by +pressing the undo-key (usually ctr-z). They can also be redone afterwards. +The undo/redo framework is cleared, when setData() sets up a new +content for the editor. + +This widget can only handle small amounts of data. The size has to be below 10 +megabytes, otherwise the scroll sliders ard not shown and you can't scroll any +more. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Property selection color sets (setSelectionColor()) the backgorund + color of selected text areas. You can also read the color + (selectionColor()). + */ + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwrite mode the user will overwrite existing data. The + size of data will be constant. In insert mode the size will grow, when inserting + new data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + + /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode + in which the editor works. In readonly mode the the user can only navigate + through the data and select data; modifying is not possible. This + property's default is false. + */ + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + Q_PROPERTY(QFont font READ font WRITE setFont) + + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + In overwrite mode, the existing bytes will be overwriten with 0x00. + */ + void remove(int pos, int len=1); + + /*! Gives back a formatted image of the content of QHexEdit + */ + QString toReadableString(); + + /*! Gives back a formatted image of the selected content of QHexEdit + */ + QString selectionToReadableString(); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setSelectionColor(QColor const &color); + QColor selectionColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + void setReadOnly(bool); + bool isReadOnly(); + const QFont &font() const; + void setFont(const QFont &); + /*! \endcond docNever */ + +public slots: + /*! Redoes the last operation. If there is no operation to redo, i.e. + there is no redo step in the undo/redo history, nothing happens. + */ + void redo(); + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + + /*! Undoes the last operation. If there is no operation to undo, i.e. + there is no undo step in the undo/redo history, nothing happens. + */ + void undo(); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddressChanged(int address); + + /*! Contains the size of the data to edit. */ + void currentSizeChanged(int size); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..2f046bb --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,800 @@ +#include + +#include "qhexedit_p.h" +#include "commands.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _undoStack = new QUndoStack(this); + + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setReadOnly(false); + setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); + setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); + setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); + setFont(QFont("Courier", 10)); + + _size = 0; + resetSelection(0); + + setFocusPolicy(Qt::StrongFocus); + + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + _cursorTimer.setInterval(500); + _cursorTimer.start(); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _xData.setAddressOffset(offset); + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _xData.addressOffset(); +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _xData.setData(data); + _undoStack->clear(); + adjust(); + setCursorPos(0); +} + +QByteArray QHexEditPrivate::data() +{ + return _xData.data(); +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setSelectionColor(const QColor &color) +{ + _selectionColor = color; + update(); +} + +QColor QHexEditPrivate::selectionColor() +{ + return _selectionColor; +} + +void QHexEditPrivate::setReadOnly(bool readOnly) +{ + _readOnly = readOnly; +} + +bool QHexEditPrivate::isReadOnly() +{ + return _readOnly; +} + +XByteArray & QHexEditPrivate::xData() +{ + return _xData; +} + +void QHexEditPrivate::insert(int index, const QByteArray & ba) +{ + if (ba.length() > 0) + { + if (_overwriteMode) + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } +} + +void QHexEditPrivate::insert(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::remove(int index, int len) +{ + if (len > 0) + { + if (len == 1) + { + if (_overwriteMode) + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + else + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + } + else + { + QByteArray ba = QByteArray(len, char(0)); + if (_overwriteMode) + { + QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } + } +} + +void QHexEditPrivate::replace(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::replace(int index, const QByteArray & ba) +{ + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + _xData.setAddressWidth(addressWidth); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + _overwriteMode = overwriteMode; +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::redo() +{ + _undoStack->redo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +void QHexEditPrivate::undo() +{ + _undoStack->undo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +QString QHexEditPrivate::toRedableString() +{ + return _xData.toRedableString(); +} + + +QString QHexEditPrivate::selectionToReadableString() +{ + return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + +/*****************************************************************************/ +/* Cursor movements */ +/*****************************************************************************/ + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + { + setCursorPos(_cursorPosition - 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfLine)) + { + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfLine)) + { + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousLine)) + { + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousPage)) + { + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_xData.size() * 2); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfDocument)) + { + setCursorPos(0); + resetSelection(_cursorPosition); + } + +/*****************************************************************************/ +/* Select commands */ +/*****************************************************************************/ + if (event->matches(QKeySequence::SelectAll)) + { + resetSelection(0); + setSelection(2*_xData.size() + 1); + } + if (event->matches(QKeySequence::SelectNextChar)) + { + int pos = _cursorPosition + 1; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousChar)) + { + int pos = _cursorPosition - 1; + setSelection(pos); + setCursorPos(pos); + } + if (event->matches(QKeySequence::SelectEndOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousLine)) + { + int pos = _cursorPosition - (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectNextLine)) + { + int pos = _cursorPosition + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + + if (event->matches(QKeySequence::SelectNextPage)) + { + int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousPage)) + { + int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectEndOfDocument)) + { + int pos = _xData.size() * 2; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfDocument)) + { + int pos = 0; + setCursorPos(pos); + setSelection(pos); + } + +/*****************************************************************************/ +/* Edit Commands */ +/*****************************************************************************/ +if (!_readOnly) +{ + /* Hex input */ + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + + // If insert mode, then insert a byte + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + } + + // Change content + if (_xData.size() > 0) + { + QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + + replace(posBa, QByteArray().fromHex(hexValue)[0]); + + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + } + + /* Cut & Paste */ + if (event->matches(QKeySequence::Cut)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append("\n"); + } + remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + setCursorPos(getSelectionBegin()); + resetSelection(getSelectionBegin()); + } + + if (event->matches(QKeySequence::Paste)) + { + QClipboard *clipboard = QApplication::clipboard(); + QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); + insert(_cursorPosition / 2, ba); + setCursorPos(_cursorPosition + 2 * ba.length()); + resetSelection(getSelectionBegin()); + } + + + /* Delete char */ + if (event->matches(QKeySequence::Delete)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (_overwriteMode) + replace(posBa, char(0)); + else + remove(posBa, 1); + } + } + + /* Backspace */ + if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (posBa > 0) + { + if (_overwriteMode) + replace(posBa - 1, char(0)); + else + remove(posBa - 1, 1); + setCursorPos(_cursorPosition - 2); + } + } + } + + /* undo */ + if (event->matches(QKeySequence::Undo)) + { + undo(); + } + + /* redo */ + if (event->matches(QKeySequence::Redo)) + { + redo(); + } + + } + + if (event->matches(QKeySequence::Copy)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append('\n'); + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + } + + // Switch between insert/overwrite mode + if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) + { + _overwriteMode = !_overwriteMode; + setCursorPos(_cursorPosition); + overwriteModeChanged(_overwriteMode); + } + + _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); + update(); +} + +void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int actPos = cursorPos(event->pos()); + setCursorPos(actPos); + setSelection(actPos); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int cPos = cursorPos(event->pos()); + resetSelection(cPos); + setCursorPos(cPos); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _xData.size()) + lastLineIdx = _xData.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); + QBrush selected = QBrush(_selectionColor); + QPen colSelected = QPen(Qt::white); + QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); + + painter.setBackgroundMode(Qt::TransparentMode); + + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + int posBa = lineIdx + colIdx; + if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) + { + painter.setBackground(selected); + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setPen(colSelected); + } + else + { + if (_highlighting) + { + // hilight diff bytes + painter.setBackground(highLighted); + if (_xData.dataChanged(posBa)) + { + painter.setPen(colHighlighted); + painter.setBackgroundMode(Qt::OpaqueMode); + } + else + { + painter.setPen(colStandard); + painter.setBackgroundMode(Qt::TransparentMode); + } + } + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + + } + } + painter.setBackgroundMode(Qt::TransparentMode); + painter.setPen(this->palette().color(QPalette::WindowText)); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + int xPosAscii = _xPosAscii; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); + xPosAscii += _charWidth; + } + } + } + + // paint cursor + if (_blink) + { + if (_overwriteMode) + painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); + else + painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); + } + + if (_size != _xData.size()) + { + _size = _xData.size(); + emit currentSizeChanged(_size); + } +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_xData.size() * 2 - 1)) + position = _xData.size() * 2 - 1; + } else { + if (position > (_xData.size() * 2)) + position = _xData.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddressChanged(_cursorPosition/2); +} + +int QHexEditPrivate::cursorPos(QPoint pos) +{ + int result = -1; + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; + result = x + y; + } + return result; +} + +int QHexEditPrivate::cursorPos() +{ + return _cursorPosition; +} + +void QHexEditPrivate::resetSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + _selectionInit = pos; + _selectionBegin = pos; + _selectionEnd = pos; +} + +void QHexEditPrivate::setSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + if (pos >= _selectionInit) + { + _selectionEnd = pos; + _selectionBegin = _selectionInit; + } + else + { + _selectionBegin = pos; + _selectionEnd = _selectionInit; + } +} + +int QHexEditPrivate::getSelectionBegin() +{ + return _selectionBegin; +} + +int QHexEditPrivate::getSelectionEnd() +{ + return _selectionEnd; +} + + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..b802af3 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,120 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include +#include "xbytearray.h" + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setAddressOffset(int offset); + int addressOffset(); + + void setCursorPos(int position); + int cursorPos(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void setReadOnly(bool readOnly); + bool isReadOnly(); + + void setSelectionColor(QColor const &color); + QColor selectionColor(); + + XByteArray & xData(); + + void insert(int index, const QByteArray & ba); + void insert(int index, char ch); + void remove(int index, int len=1); + void replace(int index, char ch); + void replace(int index, const QByteArray & ba); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + + void undo(); + void redo(); + + QString toRedableString(); + QString selectionToReadableString(); + +signals: + void currentAddressChanged(int address); + void currentSizeChanged(int size); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mousePressEvent(QMouseEvent * event); + + void paintEvent(QPaintEvent *event); + + int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION + + void resetSelection(int pos); + void setSelection(int pos); // set min (if below init) or max (if greater init) + int getSelectionBegin(); + int getSelectionEnd(); + + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QColor _highlightingColor; + QColor _selectionColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + QUndoStack *_undoStack; + + XByteArray _xData; // Hält den Inhalt des Hex Editors + + bool _blink; // true: then cursor blinks + bool _renderingRequired; // Flag to store that rendering is necessary + bool _addressArea; // left area of QHexEdit + bool _asciiArea; // medium area + bool _highlighting; // highlighting of changed bytes + bool _overwriteMode; + bool _readOnly; // true: the user can only look and navigate + + int _charWidth, _charHeight; // char dimensions (dpendend on font) + int _cursorX, _cursorY; // graphics position of the cursor + int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) + int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas + + int _selectionBegin; // First selected char + int _selectionEnd; // Last selected char + int _selectionInit; // That's, where we pressed the mouse button + + int _size; +}; + +/** \endcond docNever */ + +#endif + diff --git a/extra/qhexedit2/src/xbytearray.cpp b/extra/qhexedit2/src/xbytearray.cpp new file mode 100644 index 0000000..ec8bf3d --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.cpp @@ -0,0 +1,167 @@ +#include "xbytearray.h" + +XByteArray::XByteArray() +{ + _oldSize = -99; + _addressNumbers = 4; + _addressOffset = 0; + +} + +int XByteArray::addressOffset() +{ + return _addressOffset; +} + +void XByteArray::setAddressOffset(int offset) +{ + _addressOffset = offset; +} + +int XByteArray::addressWidth() +{ + return _addressNumbers; +} + +void XByteArray::setAddressWidth(int width) +{ + if ((width >= 0) and (width<=6)) + { + _addressNumbers = width; + } +} + +QByteArray & XByteArray::data() +{ + return _data; +} + +void XByteArray::setData(QByteArray data) +{ + _data = data; + _changedData = QByteArray(data.length(), char(0)); +} + +bool XByteArray::dataChanged(int i) +{ + return bool(_changedData[i]); +} + +QByteArray XByteArray::dataChanged(int i, int len) +{ + return _changedData.mid(i, len); +} + +void XByteArray::setDataChanged(int i, bool state) +{ + _changedData[i] = char(state); +} + +void XByteArray::setDataChanged(int i, const QByteArray & state) +{ + int length = state.length(); + int len; + if ((i + length) > _changedData.length()) + len = _changedData.length() - i; + else + len = length; + _changedData.replace(i, len, state); +} + +int XByteArray::realAddressNumbers() +{ + if (_oldSize != _data.size()) + { + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + } + return _realAddressNumbers; +} + +int XByteArray::size() +{ + return _data.size(); +} + +QByteArray & XByteArray::insert(int i, char ch) +{ + _data.insert(i, ch); + _changedData.insert(i, char(1)); + return _data; +} + +QByteArray & XByteArray::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _changedData.insert(i, QByteArray(ba.length(), char(1))); + return _data; +} + +QByteArray & XByteArray::remove(int i, int len) +{ + _data.remove(i, len); + _changedData.remove(i, len); + return _data; +} + +QByteArray & XByteArray::replace(int index, char ch) +{ + _data[index] = ch; + _changedData[index] = char(1); + return _data; +} + +QByteArray & XByteArray::replace(int index, const QByteArray & ba) +{ + int len = ba.length(); + return replace(index, len, ba); +} + +QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) +{ + int len; + if ((index + length) > _data.length()) + len = _data.length() - index; + else + len = length; + _data.replace(index, len, ba.mid(0, len)); + _changedData.replace(index, len, QByteArray(len, char(1))); + return _data; +} + +QChar XByteArray::asciiChar(int index) +{ + char ch = _data[index]; + if ((ch < 0x20) or (ch > 0x7e)) + ch = '.'; + return QChar(ch); +} + +QString XByteArray::toRedableString(int start, int end) +{ + int adrWidth = realAddressNumbers(); + if (_addressNumbers > adrWidth) + adrWidth = _addressNumbers; + if (end < 0) + end = _data.size(); + + QString result; + for (int i=start; i < end; i += 16) + { + QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); + QString hexStr; + QString ascStr; + for (int j=0; j<16; j++) + { + if ((i + j) < _data.size()) + { + hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); + ascStr.append(asciiChar(i+j)); + } + } + result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; + } + return result; +} diff --git a/extra/qhexedit2/src/xbytearray.h b/extra/qhexedit2/src/xbytearray.h new file mode 100644 index 0000000..2b67c61 --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.h @@ -0,0 +1,66 @@ +#ifndef XBYTEARRAY_H +#define XBYTEARRAY_H + +/** \cond docNever */ + +#include + +/*! XByteArray represents the content of QHexEcit. +XByteArray comprehend the data itself and informations to store if it was +changed. The QHexEdit component uses these informations to perform nice +rendering of the data + +XByteArray also provides some functionality to insert, replace and remove +single chars and QByteArras. Additionally some functions support rendering +and converting to readable strings. +*/ +class XByteArray +{ +public: + explicit XByteArray(); + + int addressOffset(); + void setAddressOffset(int offset); + + int addressWidth(); + void setAddressWidth(int width); + + QByteArray & data(); + void setData(QByteArray data); + + bool dataChanged(int i); + QByteArray dataChanged(int i, int len); + void setDataChanged(int i, bool state); + void setDataChanged(int i, const QByteArray & state); + + int realAddressNumbers(); + int size(); + + QByteArray & insert(int i, char ch); + QByteArray & insert(int i, const QByteArray & ba); + + QByteArray & remove(int pos, int len); + + QByteArray & replace(int index, char ch); + QByteArray & replace(int index, const QByteArray & ba); + QByteArray & replace(int index, int length, const QByteArray & ba); + + QChar asciiChar(int index); + QString toRedableString(int start=0, int end=-1); + +signals: + +public slots: + +private: + QByteArray _data; + QByteArray _changedData; + + int _addressNumbers; // wanted width of address area + int _addressOffset; // will be added to the real addres inside bytearray + int _realAddressNumbers; // real width of address area (can be greater then wanted width) + int _oldSize; // size of data +}; + +/** \endcond docNever */ +#endif // XBYTEARRAY_H diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..0f9d987 --- /dev/null +++ b/ost.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..f53fa81 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..7c7789e --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[PB_HDR_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..e1fbdf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,39 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..2d1bd63 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,291 @@ +/* +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 +*/ + +//#include "pbhelper.h" +#include "rpcserver.h" +#include "pbqtio.h" + +#include + +RpcServer::RpcServer() +{ + server = NULL; + clientSock = NULL; + + service = NULL; + + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false +} + +RpcServer::~RpcServer() +{ + if (server) + delete server; +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + server = new QTcpServer(); + connect(server, SIGNAL(newConnection()), this, SLOT(when_newConnection())); + if (!server->listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + server->errorString().toAscii().constData()); + errorString_ = QString("Error starting Ostinato server: %1").arg( + server->errorString()); + return false; + } + + qDebug("The server is running on %s: %d", + server->serverAddress().toString().toAscii().constData(), + server->serverPort()); + errorString_ = QString(); + return true; +} + +QString RpcServer::errorString() +{ + return errorString_; +} + +void RpcServer::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcServer::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + +_exit: + delete controller; + isPending = false; +} + +void RpcServer::when_newConnection() +{ + if (clientSock) + { + QTcpSocket *sock; + + qDebug("already connected, no new connections will be accepted"); + + // Accept and close connection + //! \todo (MED) Send reason msg to client + sock = server->nextPendingConnection(); + sock->disconnectFromHost(); + sock->deleteLater(); + goto _exit; + } + + clientSock = server->nextPendingConnection(); + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + +_exit: + return; +} + +void RpcServer::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + delete inStream; + delete outStream; + + clientSock->deleteLater(); + clientSock = NULL; +} + +void RpcServer::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcServer::when_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit2; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcServer::done, controller)); + + parsing = false; + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..76f179a --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include +#include +#include +#include + +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + + +class RpcServer : public QObject +{ + Q_OBJECT + + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..4aa97c6 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,594 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#define __STDC_FORMAT_MACROS + +#include "abstractport.h" + +#include "../common/streambase.h" +#include "../common/abstractprotocol.h" + +#include +#include + +#include +#include +#include + +AbstractPort::AbstractPort(int id, const char *device) +{ + isUsable_ = true; + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + minPacketSetSize_ = 1; + + maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + if (port.has_transmit_mode()) + data_.set_transmit_mode(port.transmit_mode()); + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::addNote(QString note) +{ + QString notes = QString::fromStdString(data_.notes()); + + note.prepend("
  • "); + note.append("
  • "); + + if (notes.isEmpty()) + notes="Limitation(s)
      "; + else + notes.remove("
    "); + + notes.append(note); + notes.append(""); + + data_.set_notes(notes.toStdString()); +} + +void AbstractPort::updatePacketList() +{ + switch(data_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + updatePacketListSequential(); + break; + case OstProto::kInterleavedTransmit: + updatePacketListInterleaved(); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } +} + +void AbstractPort::updatePacketListSequential() +{ + long sec = 0; + long nsec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + int len; + ulong n, x, y; + ulong burstSize; + double ibg = 0; + quint64 ibg1 = 0, ibg2 = 0; + quint64 nb1 = 0, nb2 = 0; + double ipg = 0; + quint64 ipg1 = 0, ipg2 = 0; + quint64 npx1 = 0, npx2 = 0; + quint64 npy1 = 0, npy2 = 0; + quint64 loopDelay; + ulong frameVariableCount = streamList_[i]->frameVariableCount(); + + // We derive n, x, y such that + // n * x + y = total number of packets to be sent + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + burstSize = streamList_[i]->burstSize(); + x = AbstractProtocol::lcm(frameVariableCount, burstSize); + n = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) / x; + y = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) % x; + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + ibg1 = quint64(ceil(ibg)); + ibg2 = quint64(floor(ibg)); + nb1 = quint64((ibg - double(ibg2)) * double(x)); + nb2 = x - nb1; + } + loopDelay = ibg2; + break; + case OstProto::StreamControl::e_su_packets: + x = frameVariableCount; + n = 2; + while (x < minPacketSetSize_) + x = frameVariableCount*n++; + n = streamList_[i]->numPackets() / x; + y = streamList_[i]->numPackets() % x; + burstSize = x + y; + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + ipg1 = quint64(ceil(ipg)); + ipg2 = quint64(floor(ipg)); + npx1 = quint64((ipg - double(ipg2)) * double(x)); + npx2 = x - npx1; + npy1 = quint64((ipg - double(ipg2)) * double(y)); + npy2 = y - npy1; + } + loopDelay = ipg2; + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + + qDebug("\nframeVariableCount = %lu", frameVariableCount); + qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", + n, x, y, burstSize); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, ibg1); + qDebug("nb1 = %" PRIu64, nb1); + qDebug("ibg2 = %" PRIu64, ibg2); + qDebug("nb2 = %" PRIu64 "\n", nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, ipg1); + qDebug("npx1 = %" PRIu64, npx1); + qDebug("npy1 = %" PRIu64, npy1); + qDebug("ipg2 = %" PRIu64, ipg2); + qDebug("npx2 = %" PRIu64, npx2); + qDebug("npy2 = %" PRIu64 "\n", npy2); + + if (n > 1) + loopNextPacketSet(x, n, 0, loopDelay); + else if (n == 0) + x = 0; + + for (uint j = 0; j < (x+y); j++) + { + + if (j == 0 || frameVariableCount > 1) + { + len = streamList_[i]->frameValue( + pktBuf_, sizeof(pktBuf_), j); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d) sec = %lu nsec = %lu", + i, j, sec, nsec); + + appendToPacketList(sec, nsec, pktBuf_, len); + + if ((j > 0) && (((j+1) % burstSize) == 0)) + { + nsec += (j < nb1) ? ibg1 : ibg2; + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + else + { + if (j < x) + nsec += (j < npx1) ? ipg1 : ipg2; + else + nsec += ((j-x) < npy1) ? ipg1 : ipg2; + + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + } + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, 0, + streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg1 : ipg1); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::updatePacketListInterleaved() +{ + int numStreams = 0; + quint64 minGap = ULLONG_MAX; + quint64 duration = quint64(1e9); + QList ibg1, ibg2; + QList nb1, nb2; + QList ipg1, ipg2; + QList np1, np2; + QList schedSec, schedNsec; + QList pktCount, burstCount; + QList burstSize; + QList isVariable; + QList pktBuf; + QList pktLen; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (!streamList_[i]->isEnabled()) + continue; + + double numBursts = 0; + double numPackets = 0; + + quint64 _burstSize; + double ibg = 0; + quint64 _ibg1 = 0, _ibg2 = 0; + quint64 _nb1 = 0, _nb2 = 0; + double ipg = 0; + quint64 _ipg1 = 0, _ipg2 = 0; + quint64 _np1 = 0, _np2 = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->burstRate(); + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + _ibg1 = quint64(ceil(ibg)); + _ibg2 = quint64(floor(ibg)); + _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); + _nb2 = quint64(numBursts) - _nb1; + _burstSize = streamList_[i]->burstSize(); + } + break; + case OstProto::StreamControl::e_su_packets: + numPackets = streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + _ipg1 = llrint(ceil(ipg)); + _ipg2 = quint64(floor(ipg)); + _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); + _np2 = quint64(numPackets) - _np1; + _burstSize = 1; + } + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, _ibg1); + qDebug("nb1 = %" PRIu64, _nb1); + qDebug("ibg2 = %" PRIu64, _ibg2); + qDebug("nb2 = %" PRIu64 "\n", _nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, _ipg1); + qDebug("np1 = %" PRIu64, _np1); + qDebug("ipg2 = %" PRIu64, _ipg2); + qDebug("np2 = %" PRIu64 "\n", _np2); + + + if (_ibg2 && (_ibg2 < minGap)) + minGap = _ibg2; + + if (_ibg1 && (_ibg1 > duration)) + duration = _ibg1; + + ibg1.append(_ibg1); + ibg2.append(_ibg2); + + nb1.append(_nb1); + nb2.append(_nb1); + + burstSize.append(_burstSize); + + if (_ipg2 && (_ipg2 < minGap)) + minGap = _ipg2; + + if (_np1) + { + if (_ipg1 && (_ipg1 > duration)) + duration = _ipg1; + } + else + { + if (_ipg2 && (_ipg2 > duration)) + duration = _ipg2; + } + + ipg1.append(_ipg1); + ipg2.append(_ipg2); + + np1.append(_np1); + np2.append(_np1); + + schedSec.append(0); + schedNsec.append(0); + + pktCount.append(0); + burstCount.append(0); + + if (streamList_[i]->isFrameVariable()) + { + isVariable.append(true); + pktBuf.append(QByteArray()); + pktLen.append(0); + } + else + { + isVariable.append(false); + pktBuf.append(QByteArray()); + pktBuf.last().resize(kMaxPktSize); + pktLen.append(streamList_[i]->frameValue( + (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); + } + + numStreams++; + } // for i + + qDebug("minGap = %" PRIu64, minGap); + qDebug("duration = %" PRIu64, duration); + + uchar* buf; + int len; + quint64 durSec = duration/ulong(1e9); + quint64 durNsec = duration % ulong(1e9); + quint64 sec = 0; + quint64 nsec = 0; + quint64 lastPktTxSec = 0; + quint64 lastPktTxNsec = 0; + do + { + for (int i = 0; i < numStreams; i++) + { + // If a packet is not scheduled yet, look at the next stream + if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) + continue; + + for (uint j = 0; j < burstSize[i]; j++) + { + if (isVariable.at(i)) + { + buf = pktBuf_; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), + pktCount[i]); + } + else + { + buf = (uchar*) pktBuf.at(i).data(); + len = pktLen.at(i); + } + + if (len <= 0) + continue; + + qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); + appendToPacketList(sec, nsec, buf, len); + lastPktTxSec = sec; + lastPktTxNsec = nsec; + + pktCount[i]++; + schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? + ipg1.at(i) : ipg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + burstCount[i]++; + schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? + ibg1.at(i) : ibg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + nsec += minGap; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } while ((sec < durSec) || (nsec < durNsec)); + + qint64 delaySec = durSec - lastPktTxSec; + qint64 delayNsec = durNsec - lastPktTxNsec; + while (delayNsec < 0) + { + delayNsec += long(1e9); + delaySec--; + } + qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); + setPacketListLoopMode(true, delaySec, delayNsec); + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? + stats_.rxPkts - epochStats_.rxPkts : + stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); + stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? + stats_.rxBytes - epochStats_.rxBytes : + stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? + stats_.txPkts - epochStats_.txPkts : + stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); + stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? + stats_.txBytes - epochStats_.txBytes : + stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; + + stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? + stats_.rxDrops - epochStats_.rxDrops : + stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); + stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? + stats_.rxErrors - epochStats_.rxErrors : + stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); + stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? + stats_.rxFifoErrors - epochStats_.rxFifoErrors : + stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); + stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? + stats_.rxFrameErrors - epochStats_.rxFrameErrors : + stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..44f0c1d --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 rxDrops; + quint64 rxErrors; + quint64 rxFifoErrors; + quint64 rxFrameErrors; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + bool isUsable() { return isUsable_; } + + virtual void init(); + + int id() { return data_.port_id().id(); } + const char* name() { return data_.name().c_str(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) = 0; + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, + quint64 secDelay, quint64 nsecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + void addNote(QString note); + + void updatePacketListSequential(); + void updatePacketListInterleaved(); + + bool isUsable_; + OstProto::Port data_; + OstProto::LinkState linkState_; + ulong minPacketSetSize_; + + quint64 maxStatsValue_; + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/bsdport.cpp b/server/bsdport.cpp new file mode 100644 index 0000000..4ac9ab7 --- /dev/null +++ b/server/bsdport.cpp @@ -0,0 +1,355 @@ +/* +Copyright (C) 2012 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 "bsdport.h" + +#ifdef Q_OS_BSD4 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#define ifr_flagshigh ifr_flags +#define IFF_PPROMISC (IFF_PROMISC << 16) +#endif + +QList BsdPort::allPorts_; +BsdPort::StatsMonitor *BsdPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +BsdPort::BsdPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Bsd + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = ULONG_MAX; +} + +BsdPort::~BsdPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if (ifr.ifr_flagshigh & promisc) + { + ifr.ifr_flagshigh &= ~promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + else + qDebug("Cleared promisc successfully"); + } + else + qDebug("clear_promisc is set but IFF_PPROMISC is not?"); + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void BsdPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +bool BsdPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool BsdPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +BsdPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; +} + +void BsdPort::StatsMonitor::run() +{ + int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + const int mibLen = sizeof(mib)/sizeof(mib[0]); + QHash portStats; + QHash linkState; + int sd; + QByteArray buf; + size_t len; + char *p, *end; + int count; + struct ifreq ifr; + + // + // We first setup stuff before we start polling for stats + // + if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); + return; + } + + qDebug("sysctl mib returns reqd len = %d\n", (int) len); + len *= 2; // for extra room, just in case! + buf.fill('\0', len); + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); + return; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + + // + // Populate the port stats hash table + // + p = buf.data(); + end = p + len; + count = 0; + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); + + if (ifm->ifm_type == RTM_IFINFO) + { + char ifname[1024]; + + strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + + qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); + foreach(BsdPort* port, allPorts_) + { + if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) + { + Q_ASSERT(ifm->ifm_index == sdl->sdl_index); + portStats[uint(ifm->ifm_index)] = &(port->stats_); + linkState[uint(ifm->ifm_index)] = &(port->linkState_); + + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if ((ifr.ifr_flagshigh & promisc) == 0) + { + ifr.ifr_flagshigh |= promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + { + qDebug("%s: set promisc successful", + port->name()); + port->clearPromisc_ = true; + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + else + qDebug("%s: promisc already set", port->name()); + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + count++; + } + p += ifm->ifm_msglen; + } + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in NET_RT_IFLIST - no stats will be available"); + return; + } + + close(sd); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); + goto _try_later; + } + + p = buf.data(); + end = p + len; + + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + AbstractPort::PortStats *stats; + + if (ifm->ifm_type != RTM_IFINFO) + goto _next; + + stats = portStats[ifm->ifm_index]; + if (stats) + { + struct if_data *ifd = &(ifm->ifm_data); + OstProto::LinkState *state = linkState[ifm->ifm_index]; + u_long in_packets; + + Q_ASSERT(state); +#ifdef Q_OS_MAC + *state = ifm->ifm_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; +#else + *state = (OstProto::LinkState) ifd->ifi_link_state; +#endif + + in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxPps = + ((in_packets >= stats->rxPkts) ? + in_packets - stats->rxPkts : + in_packets + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((ifd->ifi_ibytes >= stats->rxBytes) ? + ifd->ifi_ibytes - stats->rxBytes : + ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = in_packets; + stats->rxBytes = ifd->ifi_ibytes; + stats->txPps = + ((ifd->ifi_opackets >= stats->txPkts) ? + ifd->ifi_opackets - stats->txPkts : + ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((ifd->ifi_obytes >= stats->txBytes) ? + ifd->ifi_obytes - stats->txBytes : + ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = ifd->ifi_opackets; + stats->txBytes = ifd->ifi_obytes; + + stats->rxDrops = ifd->ifi_iqdrops; + stats->rxErrors = ifd->ifi_ierrors; + } +_next: + p += ifm->ifm_msglen; + } +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); +} + +void BsdPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/bsdport.h b/server/bsdport.h new file mode 100644 index 0000000..776c39a --- /dev/null +++ b/server/bsdport.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2012 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 +*/ + +#ifndef _SERVER_BSD_PORT_H +#define _SERVER_BSD_PORT_H + +#include + +#ifdef Q_OS_BSD4 + +#include "pcapport.h" + +class BsdPort : public PcapPort +{ +public: + BsdPort(int id, const char *device); + ~BsdPort(); + + void init(); + + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..2e60bf8 --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,53 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QObject *parent) + : QObject(parent) +{ + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + qCritical(qPrintable(rpcServer->errorString())); + return false; + } + + return true; +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..9474207 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,40 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QObject +{ + Q_OBJECT +public: + Drone(QObject *parent = 0); + ~Drone(); + bool init(); + +private: + RpcServer *rpcServer; + OstProto::OstService *service; +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..be69521 --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,47 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +QT -= gui +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + CONFIG += console + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lm +LIBS += -lprotobuf +HEADERS += drone.h +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + bsdport.cpp \ + linuxport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..7d8453b --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,83 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +#include + +#include + +#ifdef Q_OS_UNIX +#include +#endif + +extern ProtocolManager *OstProtocolManager; + +int myport; + +void cleanup(int /*signum*/) +{ + QCoreApplication::instance()->exit(-1); +} + +int main(int argc, char *argv[]) +{ + int exitCode = 0; + QCoreApplication app(argc, argv); + Drone *drone = new Drone(); + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone->objectName()); + + // TODO: command line options + // -v (--version) + // -h (--help) + // -p (--portnum) + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone->init()) + { + exitCode = -1; + goto _exit; + } + +#ifdef Q_OS_UNIX + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup; + if (sigaction(SIGTERM, &sa, NULL)) + qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); + if (sigaction(SIGINT, &sa, NULL)) + qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); +#endif + + exitCode = app.exec(); + +_exit: + delete drone; + delete OstProtocolManager; + + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + +#include "linuxport.h" + +#ifdef Q_OS_LINUX + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QList LinuxPort::allPorts_; +LinuxPort::StatsMonitor *LinuxPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +LinuxPort::LinuxPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Linux + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = 0xffffffff; +} + +LinuxPort::~LinuxPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if (ifr.ifr_flags & IFF_PROMISC) + { + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + } + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void LinuxPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +OstProto::LinkState LinuxPort::linkState() +{ + return linkState_; +} + +bool LinuxPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool LinuxPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +LinuxPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; + ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(ioctlSocket_ >= 0); +} + +LinuxPort::StatsMonitor::~StatsMonitor() +{ + close(ioctlSocket_); +} + +void LinuxPort::StatsMonitor::run() +{ + if (netlinkStats() < 0) + { + qDebug("netlink stats not available - using /proc stats"); + procStats(); + } +} + +void LinuxPort::StatsMonitor::procStats() +{ + PortStats **portStats; + int fd; + QByteArray buf; + int len; + char *p, *end; + int count, index; + const char* fmtopt[] = { + "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", + }; + const char *fmt; + + // + // We first setup stuff before we start polling for stats + // + fd = open("/proc/net/dev", O_RDONLY); + if (fd < 0) + { + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; + } + + buf.fill('\0', 8192); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + qWarning("initial buffer size is too small. no stats will be available"); + return; + } + + p = buf.data(); + end = p + len; + + // Select scanf format + if (strstr(buf, "compressed")) + fmt = fmtopt[0]; + else + fmt = fmtopt[1]; + + // Count number of lines - number of ports is 2 less than number of lines + count = 0; + while (p < end) + { + if (*p == '\n') + count++; + p++; + } + count -= 2; + + if (count <= 0) + { + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; + } + + portStats = (PortStats**) calloc(count, sizeof(PortStats)); + Q_ASSERT(portStats != NULL); + + // + // Populate the port stats array + // + p = buf.data(); + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + char* q; + + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; + + q = p; + + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) + { + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; + p++; + } + Q_ASSERT(index == count); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; + } + + p = buf.data(); + end = p + len; + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 rxErrors, rxDrops, rxFifo, rxFrame; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, + &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + stats->rxPps = + ((rxPkts >= stats->rxPkts) ? + rxPkts - stats->rxPkts : + rxPkts + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rxBytes >= stats->rxBytes) ? + rxBytes - stats->rxBytes : + rxBytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = + ((txPkts >= stats->txPkts) ? + txPkts - stats->txPkts : + txPkts + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((txBytes >= stats->txBytes) ? + txBytes - stats->txBytes : + txBytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + + stats->rxDrops = rxDrops; + stats->rxErrors = rxErrors; + stats->rxFifoErrors = rxFifo; + stats->rxFrameErrors = rxFrame; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); + } + + free(portStats); +} + +int LinuxPort::StatsMonitor::netlinkStats() +{ + QHash portStats; + QHash linkState; + int fd; + struct sockaddr_nl local; + struct sockaddr_nl kernel; + QByteArray buf; + int len, count; + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtg; + } ifListReq; + struct iovec iov; + struct msghdr msg; + struct nlmsghdr *nlm; + bool done = false; + + // + // We first setup stuff before we start polling for stats + // + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + qWarning("Unable to open netlink socket (errno %d)", errno); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) + { + qWarning("Unable to bind netlink socket (errno %d)", errno); + return -1; + } + + memset(&ifListReq, 0, sizeof(ifListReq)); + ifListReq.nlh.nlmsg_len = sizeof(ifListReq); + ifListReq.nlh.nlmsg_type = RTM_GETLINK; + ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifListReq.nlh.nlmsg_pid = 0; + ifListReq.rtg.rtgen_family = AF_PACKET; + + buf.fill('\0', 1024); + + msg.msg_name = &kernel; + msg.msg_namelen = sizeof(kernel); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); + + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + return -1; + } + + // Find required size of buffer and resize accordingly + while (1) + { + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + msg.msg_flags = 0; + + // Peek at reply to check buffer size required + len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink closed the socket on my face!"); + return -1; + } + else + { + if (msg.msg_flags & MSG_TRUNC) + { + if (len == buf.size()) // Older Kernel returns truncated size + { + qDebug("netlink buffer size %d not enough", buf.size()); + qDebug("retrying with double the size"); + // Double the size and retry + buf.resize(buf.size()*2); + continue; + } + else // Newer Kernel returns actual size required + { + qDebug("netlink required buffer size = %d", len); + buf.resize(len); + continue; + } + } + else + qDebug("buffer size %d enough for netlink", buf.size()); + + break; + } + } + + count = 0; + +_retry: + msg.msg_flags = 0; + + // Actually receive the reply now + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry; + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + return -1; + } + + // + // Populate the port stats hash table + // + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + char ifname[64] = ""; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error %d", err->error); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == IFLA_IFNAME) + { + strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); + ifname[RTA_PAYLOAD(rta)] = 0; + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + + qDebug("if: %s(%d)", ifname, ifi->ifi_index); + foreach(LinuxPort* port, allPorts_) + { + if (strcmp(port->name(), ifname) == 0) + { + portStats[uint(ifi->ifi_index)] = &(port->stats_); + linkState[uint(ifi->ifi_index)] = &(port->linkState_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + count++; + break; + } + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry; + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); + return - 1; + } + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + goto _try_later; + } + + done = false; + +_retry_recv: + msg.msg_flags = 0; + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry_recv; + qWarning("netlink recv error %d", errno); + break; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + break; + } + + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error: %s", strerror(-err->error)); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + // TODO: IFLA_STATS64 + if (rta->rta_type == IFLA_STATS) + { + struct rtnl_link_stats *rtnlStats = + (struct rtnl_link_stats*) RTA_DATA(rta); + AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + OstProto::LinkState *state = linkState[ifi->ifi_index]; + + if (!stats) + break; + + stats->rxPps = + ((rtnlStats->rx_packets >= stats->rxPkts) ? + rtnlStats->rx_packets - stats->rxPkts : + rtnlStats->rx_packets + (kMaxValue32 + - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rtnlStats->rx_bytes >= stats->rxBytes) ? + rtnlStats->rx_bytes - stats->rxBytes : + rtnlStats->rx_bytes + (kMaxValue32 + - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rtnlStats->rx_packets; + stats->rxBytes = rtnlStats->rx_bytes; + stats->txPps = + ((rtnlStats->tx_packets >= stats->txPkts) ? + rtnlStats->tx_packets - stats->txPkts : + rtnlStats->tx_packets + (kMaxValue32 + - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((rtnlStats->tx_bytes >= stats->txBytes) ? + rtnlStats->tx_bytes - stats->txBytes : + rtnlStats->tx_bytes + (kMaxValue32 + - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = rtnlStats->tx_packets; + stats->txBytes = rtnlStats->tx_bytes; + + // TODO: export detailed error stats + stats->rxDrops = rtnlStats->rx_dropped + + rtnlStats->rx_missed_errors; + stats->rxErrors = rtnlStats->rx_errors; + stats->rxFifoErrors = rtnlStats->rx_fifo_errors; + stats->rxFrameErrors = rtnlStats->rx_crc_errors + + rtnlStats->rx_length_errors + + rtnlStats->rx_over_errors + + rtnlStats->rx_frame_errors; + + Q_ASSERT(state); + *state = ifi->ifi_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; + + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry_recv; + +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); + + return 0; +} + +int LinuxPort::StatsMonitor::setPromisc(const char * portName) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); + + if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) + { + return 1; + } + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + portName, strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + portName, strerror(errno)); + } + + return 0; +} + +void LinuxPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/linuxport.h b/server/linuxport.h new file mode 100644 index 0000000..2658560 --- /dev/null +++ b/server/linuxport.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SERVER_LINUX_PORT_H +#define _SERVER_LINUX_PORT_H + +#include + +#ifdef Q_OS_LINUX + +#include "pcapport.h" + +class LinuxPort : public PcapPort +{ +public: + LinuxPort(int id, const char *device); + ~LinuxPort(); + + void init(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + ~StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + int netlinkStats(); + void procStats(); + int setPromisc(const char* portName); + + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + int ioctlSocket_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/myservice.cpp b/server/myservice.cpp new file mode 100644 index 0000000..be0984a --- /dev/null +++ b/server/myservice.cpp @@ -0,0 +1,475 @@ +/* +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 +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + + s->set_rx_drops(stats.rxDrops); + s->set_rx_errors(stats.rxErrors); + s->set_rx_fifo_errors(stats.rxFifoErrors); + s->set_rx_frame_errors(stats.rxFrameErrors); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..5244956 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,788 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + + +#if defined(Q_OS_LINUX) +typedef struct timeval TimeStamp; +static void inline getTimeStamp(TimeStamp *stamp) +{ + gettimeofday(stamp, NULL); +} + +// Returns time diff in usecs between end and start +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + struct timeval diff; + long usecs; + + timersub(end, start, &diff); + + usecs = diff.tv_usec; + if (diff.tv_sec) + usecs += diff.tv_sec*1e6; + + return usecs; +} +#elif defined(Q_OS_WIN32) +static quint64 gTicksFreq; +typedef LARGE_INTEGER TimeStamp; +static void inline getTimeStamp(TimeStamp* stamp) +{ + QueryPerformanceCounter(stamp); +} + +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + if (end->QuadPart >= start->QuadPart) + return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; + else + { + // FIXME: incorrect! what's the max value for this counter before + // it rolls over? + return (start->QuadPart)*long(1e6)/gTicksFreq; + } +} +#else +typedef int TimeStamp; +static void inline getTimeStamp(TimeStamp*) {} +static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } +#endif + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitorRx_) + monitorRx_->stop(); + if (monitorTx_) + monitorTx_->stop(); + + delete capturer_; + delete transmitter_; + + if (monitorRx_) + monitorRx_->wait(); + delete monitorRx_; + + if (monitorTx_) + monitorTx_->wait(); + delete monitorTx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + bool noLocalCapture; + + direction_ = direction; + isDirectional_ = true; + isPromisc_ = true; + noLocalCapture = true; + stats_ = stats; + stop_ = false; + +_retry: +#ifdef Q_OS_WIN32 + int flags = 0; + + if (isPromisc_) + flags |= PCAP_OPENFLAG_PROMISCUOUS; + if (noLocalCapture) + flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; + + handle_ = pcap_open(device, 64 /* FIXME */, flags, + 1000 /* ms */, NULL, errbuf); +#else + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), + 1000 /* ms */, errbuf); +#endif + + if (handle_ == NULL) + { + if (isPromisc_ && QString(errbuf).contains("promiscuous")) + { + qDebug("Can't set promiscuous mode, trying non-promisc %s", device); + isPromisc_ = false; + goto _retry; + } + else if (noLocalCapture && QString(errbuf).contains("loopback")) + { + qDebug("Can't set no local capture mode %s", device); + noLocalCapture = false; + goto _retry; + } + else + goto _open_error; + } +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); +} + +PcapPort::PortMonitor::~PortMonitor() +{ + if (handle_) + pcap_close(handle_); +} + +void PcapPort::PortMonitor::run() +{ + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +void PcapPort::PortMonitor::stop() +{ + stop_ = true; + pcap_breakloop(handle()); +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE] = ""; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + gTicksFreq = ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; + if (usingInternalHandle_) + pcap_close(handle_); +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for packetSequenceList + while(packetSequenceList_.size()) + delete packetSequenceList_.takeFirst(); + + currentPacketSequence_ = NULL; + repeatSequenceStart_ = -1; + repeatSize_ = 0; + packetCount_ = 0; + + returnToQIdx_ = -1; + + setPacketListLoopMode(false, 0, 0); +} + +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) +{ + currentPacketSequence_ = new PacketSequence; + currentPacketSequence_->repeatCount_ = repeats; + currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + + repeatDelayNsec/1000; + + repeatSequenceStart_ = packetSequenceList_.size(); + repeatSize_ = size; + packetCount_ = 0; + + packetSequenceList_.append(currentPacketSequence_); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = nsec/1000; + + if (currentPacketSequence_ == NULL || + !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) + { + if (currentPacketSequence_ != NULL) + { + long usecs; + + usecs = (pktHdr.ts.tv_sec + - currentPacketSequence_->lastPacket_->ts.tv_sec) + * long(1e6); + usecs += (pktHdr.ts.tv_usec + - currentPacketSequence_->lastPacket_->ts.tv_usec); + currentPacketSequence_->usecDelay_ = usecs; + } + + //! \todo (LOW): calculate sendqueue size + currentPacketSequence_ = new PacketSequence; + + packetSequenceList_.append(currentPacketSequence_); + + // Validate that the pkt will fit inside the new currentSendQueue_ + Q_ASSERT(currentPacketSequence_->hasFreeSpace( + sizeof(pcap_pkthdr) + length)); + } + + if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) + { + op = false; + } + + packetCount_++; + if (repeatSize_ > 0 && packetCount_ == repeatSize_) + { + qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", + repeatSequenceStart_, repeatSize_); + + // Set the packetSequence repeatSize + Q_ASSERT(repeatSequenceStart_ >= 0); + Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); + + if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) + { + PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; + + currentPacketSequence_->usecDelay_ = start->usecDelay_; + start->usecDelay_ = 0; + start->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + } + + repeatSize_ = 0; + + // End current pktSeq and trigger a new pktSeq allocation for next pkt + currentPacketSequence_ = NULL; + } + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalHandle_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + long overHead = 0; // overHead should be negative or zero + + qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); + if (packetSequenceList_.size() <= 0) + return; + + for(i = 0; i < packetSequenceList_.size(); i++) { + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, + packetSequenceList_.at(i)->repeatCount_, + packetSequenceList_.at(i)->repeatSize_, + packetSequenceList_.at(i)->usecDelay_); + qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, + packetSequenceList_.at(i)->packets_, + packetSequenceList_.at(i)->usecDuration_); + } + + for(i = 0; i < packetSequenceList_.size(); i++) + { + +_restart: + int rptSz = packetSequenceList_.at(i)->repeatSize_; + int rptCnt = packetSequenceList_.at(i)->repeatCount_; + + for (int j = 0; j < rptCnt; j++) + { + for (int k = 0; k < rptSz; k++) + { + int ret; + PacketSequence *seq = packetSequenceList_.at(i+k); +#ifdef Q_OS_WIN32 + TimeStamp ovrStart, ovrEnd; + + if (seq->usecDuration_ <= long(1e6)) // 1s + { + getTimeStamp(&ovrStart); + ret = pcap_sendqueue_transmit(handle_, + seq->sendQueue_, kSyncTransmit); + if (ret >= 0) + { + stats_->txPkts += seq->packets_; + stats_->txBytes += seq->bytes_; + + getTimeStamp(&ovrEnd); + overHead += seq->usecDuration_ + - udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + } + if (stop_) + ret = -2; + } + else + { + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); + } +#else + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); +#endif + + if (ret >= 0) + { + long usecs = seq->usecDelay_ + overHead; + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + } + else + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + return; + } + } + } + } + + if (returnToQIdx_ >= 0) + { + long usecs = loopDelay_ + overHead; + + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + + i = returnToQIdx_; + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, long &overHead, int sync) +{ + TimeStamp ovrStart, ovrEnd; + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + getTimeStamp(&ovrStart); + while((char*) hdr < end) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + getTimeStamp(&ovrEnd); + + overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + usec += overHead; + if (usec > 0) + { + udelay(usec); + overHead = 0; + } + else + overHead = usec; + + ts = hdr->ts; + getTimeStamp(&ovrStart); + } + + Q_ASSERT(pktLen > 0); + + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) (pkt + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + if (stop_) + { + return -2; + } + } + + return 0; +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#if defined(Q_OS_WIN32) + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#elif defined(Q_OS_LINUX) + struct timeval delay, target, now; + + //qDebug("usec delay = %ld", usec); + + delay.tv_sec = 0; + delay.tv_usec = usec; + + while (delay.tv_usec >= 1000000) + { + delay.tv_sec++; + delay.tv_usec -= 1000000; + } + + gettimeofday(&now, NULL); + timeradd(&now, &delay, &target); + + do { + gettimeofday(&now, NULL); + } while (timercmp(&now, &target, <)); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } +_retry: + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + flag, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..d05018b --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0, 0); + } + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { + transmitter_->loopNextPacketSet(size, repeats, + repeatDelaySec, repeatDelayNsec); + } + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, nsec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) + { + transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + ~PortMonitor(); + void run(); + void stop(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } + protected: + AbstractPort::PortStats *stats_; + bool stop_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + bool isPromisc_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + + class PacketSequence + { + public: + PacketSequence() { + sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + lastPacket_ = NULL; + packets_ = 0; + bytes_ = 0; + usecDuration_ = 0; + repeatCount_ = 1; + repeatSize_ = 1; + usecDelay_ = 0; + } + ~PacketSequence() { + pcap_sendqueue_destroy(sendQueue_); + } + bool hasFreeSpace(int size) { + if ((sendQueue_->len + size) <= sendQueue_->maxlen) + return true; + else + return false; + } + int appendPacket(const struct pcap_pkthdr *pktHeader, + const uchar *pktData) { + if (lastPacket_) + { + usecDuration_ += (pktHeader->ts.tv_sec + - lastPacket_->ts.tv_sec) * long(1e6); + usecDuration_ += (pktHeader->ts.tv_usec + - lastPacket_->ts.tv_usec); + } + packets_++; + bytes_ += pktHeader->caplen; + lastPacket_ = (struct pcap_pkthdr *) + (sendQueue_->buffer + sendQueue_->len); + return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); + } + pcap_send_queue *sendQueue_; + struct pcap_pkthdr *lastPacket_; + long packets_; + long bytes_; + ulong usecDuration_; + int repeatCount_; + int repeatSize_; + long usecDelay_; + }; + + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, + int sync); + + quint64 ticksFreq_; + QList packetSequenceList_; + PacketSequence *currentPacketSequence_; + int repeatSequenceStart_; + quint64 repeatSize_; + quint64 packetCount_; + + int returnToQIdx_; + quint64 loopDelay_; + + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..2981464 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2010-2012 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 "portmanager.h" + +#include +#include + +#include "bsdport.h" +#include "linuxport.h" +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + +#if defined(Q_OS_WIN32) + port = new WinPcapPort(i, device->name); +#elif defined(Q_OS_LINUX) + port = new LinuxPort(i, device->name); +#elif defined(Q_OS_BSD4) + port = new BsdPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + + portList_.append(port); + } + + pcap_freealldevs(deviceList); + + foreach(AbstractPort *port, portList_) + port->init(); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..bbd49d8 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,226 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + monitorRx_->stop(); + monitorTx_->stop(); + monitorRx_->wait(); + monitorTx_->wait(); + + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 256; +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + if (handle()) + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + if (!stop_) + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..d5e8af1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,109 @@ + +#include "ostprotolib.h" +#include "pcapfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" +#include "settings.h" + +#include +#include +#include +#include + +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int /*argc*/, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) +{ + bool isOk; + QString error; + + if (argc != 3) + { + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; + } + + OstProto::StreamConfigList streams; + QString inFile(argv[2]); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) + { + printf("%s: %s\n", + inFile.toAscii().constData(), error.toAscii().constData()); + } + + if (!isOk) + return 1; + + return 0; +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..8dbe0cb --- /dev/null +++ b/test/test.pro @@ -0,0 +1,34 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" "../client" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 + +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..718ce69 --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.5.1 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From cabd6ad096faf2b87a1e60c2bdc374b6a11a5566 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 11 Mar 2014 06:16:41 +0530 Subject: [PATCH 210/294] NOX: Framework to separate the protocol widgets from the protocol. Changes in client to use the new framework. Changes may not compile --- client/main.cpp | 3 ++ client/mainwindow.cpp | 13 +++-- client/ostinato.pro | 13 +++-- client/stream.cpp | 44 +++------------- client/streamconfigdialog.cpp | 76 +++++++++++++++++++++++++--- client/streamconfigdialog.h | 4 ++ common/abstractprotocol.cpp | 42 +++------------- common/abstractprotocol.h | 6 +-- common/abstractprotocolconfig.h | 83 ++++++++++++++++++++++++++++++ common/ostproto.pro | 63 +++++++---------------- common/ostprotogui.pro | 73 +++++++++++++++++++++++++++ common/ostprotolib.cpp | 2 +- common/protocolmanager.cpp | 35 +++++++------ common/protocolmanager.h | 1 + common/protocolwidgetfactory.cpp | 86 ++++++++++++++++++++++++++++++++ common/protocolwidgetfactory.h | 46 +++++++++++++++++ ost.pro | 1 + 17 files changed, 441 insertions(+), 150 deletions(-) create mode 100644 common/abstractprotocolconfig.h create mode 100644 common/ostprotogui.pro create mode 100644 common/protocolwidgetfactory.cpp create mode 100644 common/protocolwidgetfactory.h diff --git a/client/main.cpp b/client/main.cpp index b9f7ae9..f8f6e0b 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -20,6 +20,7 @@ along with this program. If not, see #include "mainwindow.h" #include "../common/ostprotolib.h" #include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" #include "settings.h" #include @@ -31,6 +32,7 @@ along with this program. If not, see extern const char* version; extern const char* revision; extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; QSettings *appSettings; QMainWindow *mainWindow; @@ -58,6 +60,7 @@ int main(int argc, char* argv[]) app.setProperty("revision", revision); OstProtocolManager = new ProtocolManager(); + OstProtocolWidgetFactory = new ProtocolWidgetFactory(); /* (Portable Mode) If we have a .ini file in the same directory as the executable, we use that instead of the platform specific location diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 03a16ab..79cd708 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -100,14 +100,21 @@ MainWindow::MainWindow(QWidget *parent) MainWindow::~MainWindow() { - delete pgl; +#ifdef Q_OS_WIN32 + //! \todo - find a way to terminate cleanly + localServer_->kill(); +#else localServer_->terminate(); - localServer_->waitForFinished(); - delete localServer_; +#endif + + delete pgl; QByteArray layout = saveState(0); appSettings->setValue(kApplicationWindowLayout, layout); appSettings->setValue(kApplicationWindowGeometryKey, geometry()); + + localServer_->waitForFinished(); + delete localServer_; } void MainWindow::on_actionPreferences_triggered() diff --git a/client/ostinato.pro b/client/ostinato.pro index 0967390..d5fd459 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -7,22 +7,27 @@ QT += network script xml INCLUDEPATH += "../rpc/" "../common/" win32 { CONFIG(debug, debug|release) { - LIBS += -L"../common/debug" -lostproto + LIBS += -L"../common/debug" -lostprotogui -lostproto LIBS += -L"../rpc/debug" -lpbrpc POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ "../common/debug/libostproto.a" \ "../rpc/debug/libpbrpc.a" } else { - LIBS += -L"../common/release" -lostproto + LIBS += -L"../common/release" -lostprotogui -lostproto LIBS += -L"../rpc/release" -lpbrpc POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ "../common/release/libostproto.a" \ "../rpc/release/libpbrpc.a" } } else { - LIBS += -L"../common" -lostproto + LIBS += -L"../common" -lostprotogui -lostproto LIBS += -L"../rpc" -lpbrpc - POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" } LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 diff --git a/client/stream.cpp b/client/stream.cpp index a24c971..2305930 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -17,11 +17,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ +/*! + * \todo Remove this class + */ + #include #include #include "stream.h" -//#include "../common/protocollist.h" #include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" @@ -37,43 +40,12 @@ Stream::~Stream() void Stream::loadProtocolWidgets() { -#if 0 - //protocols.loadConfigWidgets(); - foreach(AbstractProtocol* proto, *currentFrameProtocols) - { - proto->loadConfigWidget(); - } -#else - ProtocolListIterator *iter; - - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->loadConfigWidget(); - } - delete iter; -#endif + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; } void Stream::storeProtocolWidgets() { -#if 0 - //protocols.storeConfigWidgets(); - foreach(const AbstractProtocol* proto, frameProtocol()) - { - proto->storeConfigWidget(); - _iter->toFront(); - } -#else - ProtocolListIterator *iter; - - iter = createProtocolListIterator(); - while (iter->hasNext()) - { - AbstractProtocol* p = iter->next(); - p->storeConfigWidget(); - } - delete iter; -#endif + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; } diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index e2fc3f0..087631d 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -22,13 +22,16 @@ along with this program. If not, see #include "streamconfigdialog.h" #include "stream.h" #include "abstractprotocol.h" +#include "abstractprotocolconfig.h" #include "protocollistiterator.h" #include "modeltest.h" -// FIXME(HI) - remove #include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" + extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; QRect StreamConfigDialog::lastGeometry; int StreamConfigDialog::lastTopLevelTabIndex = 0; @@ -83,6 +86,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); +#if 1 // temp mask // Setup valid subsequent protocols for L2 to L4 protocols for (int i = ProtoL2; i <= ProtoL4; i++) { @@ -129,6 +133,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, } } } +#endif mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager->protocolDatabase(), this); @@ -306,10 +311,60 @@ StreamConfigDialog::~StreamConfigDialog() for (int i = ProtoMin; i < ProtoMax; i++) delete bgProto[i]; + foreach (AbstractProtocolConfigForm* w, _protocolWidgets) { + OstProtocolWidgetFactory->deleteConfigWidget(w); + } + delete _iter; delete mpStream; } +void StreamConfigDialog::loadProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: Protocol Widgets are created on demand. Once created we + // store them in _protocolWidgets indexed by the protocol + // object's address (to ensure unique widgets for multiple + // objects of the same class). Subsequently we check + // _protocolWidgets before creating a new widget + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + _protocolWidgets.insert(p, w); + } + w->loadWidget(p); + } + delete iter; +} + +void StreamConfigDialog::storeProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: After creating a widget, we need to call loadWidget() + // to load the protocol's default values + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + w->storeWidget(p); + } + delete iter; +} + void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { if (mode == "Fixed") @@ -565,12 +620,19 @@ void StreamConfigDialog::on_twTopLevel_currentChanged(int index) w->setParent(0); } - // Repopulate the widgets + // Repopulate the widgets - create new ones, if required _iter->toFront(); while (_iter->hasNext()) { AbstractProtocol* p = _iter->next(); - tbProtocolData->addItem(p->configWidget(), p->name()); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + tbProtocolData->addItem(w, p->name()); } if (lastProtocolDataIndex < tbProtocolData->count()) @@ -892,7 +954,7 @@ void StreamConfigDialog::LoadCurrentStream() updateSelectProtocolsSimpleWidget(); updateSelectProtocolsAdvancedWidget(); - mpStream->loadProtocolWidgets(); + loadProtocolWidgets(); } // Stream Control @@ -967,7 +1029,7 @@ void StreamConfigDialog::StoreCurrentStream() // Protocols { - pStream->storeProtocolWidgets(); + storeProtocolWidgets(); } // Stream Control @@ -1005,8 +1067,8 @@ void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol // selection in TextProtocol #if 0 // FIXME: temp mask to avoid crash till we fix it - mpStream->storeProtocolWidgets(); - mpStream->loadProtocolWidgets(); + storeProtocolWidgets(); + loadProtocolWidgets(); #endif } diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h index a3214c3..5330414 100644 --- a/client/streamconfigdialog.h +++ b/client/streamconfigdialog.h @@ -37,6 +37,7 @@ along with this program. If not, see ** */ +class AbstractProtocolConfigForm; class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog { @@ -76,6 +77,7 @@ private: Stream *mpStream; ProtocolListIterator *_iter; + QHash _protocolWidgets; bool isUpdateInProgress; @@ -92,6 +94,8 @@ private: void setupUiExtra(); void LoadCurrentStream(); void StoreCurrentStream(); + void loadProtocolWidgets(); + void storeProtocolWidgets(); private slots: void on_cmbPktLenMode_currentIndexChanged(QString mode); diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp index 77def8f..230fd81 100644 --- a/common/abstractprotocol.cpp +++ b/common/abstractprotocol.cpp @@ -17,11 +17,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - #include "abstractprotocol.h" -#include "streambase.h" + #include "protocollistiterator.h" +#include "streambase.h" + +#include /*! \class AbstractProtocol @@ -46,9 +47,6 @@ along with this program. If not, see - fieldFlags() - fieldData() - setFieldData() - - configWidget() [pure virtual] - - loadConfigWidget() [pure virtual] - - storeConfigWidget() [pure virtual] Depending on certain conditions, subclasses may need to reimplement the following additional methods - @@ -107,6 +105,8 @@ AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, (file: protocol.proto) Subclasses MUST implement this function + + \todo convert this to a protected data member instead of a virtual function */ quint32 AbstractProtocol::protocolNumber() const { @@ -838,36 +838,6 @@ out: return (quint16) ~sum; } -/*! - \fn virtual QWidget* AbstractProtocol::configWidget() = 0; - - Returns the configuration widget for the protocol. The protocol retains - ownership of the configuration widget - the caller should not free it. - - In the base class this is a pure virtual function. Subclasses MUST implement - this function. See the SampleProtocol for an example -*/ - -/*! - \fn virtual void AbstractProtocol::loadConfigWidget() = 0; - - Loads data from the protocol's protobuf into the configuration widget of - the protocol - - In the base class this is a pure virtual function. Subclasses MUST implement - this function. See the SampleProtocol for an example -*/ - -/*! - \fn virtual void AbstractProtocol::storeConfigWidget() = 0; - - Stores data from the configuration widget of the protocol into the protocol's - protobuf - - In the base class this is a pure virtual function. Subclasses MUST implement - this function. See the SampleProtocol for an example -*/ - // Stein's binary GCD algo - from wikipedia quint64 AbstractProtocol::gcd(quint64 u, quint64 v) { diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index ab3b7e9..5206662 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -23,9 +23,9 @@ along with this program. If not, see #include #include #include -#include #include #include +#include //#include "../rpc/pbhelper.h" #include "protocol.pb.h" @@ -155,10 +155,6 @@ public: CksumType cksumType = CksumIp, CksumScope cksumScope = CksumScopeAllProtocols) const; - virtual QWidget* configWidget() = 0; - virtual void loadConfigWidget() = 0; - virtual void storeConfigWidget() = 0; - static quint64 lcm(quint64 u, quint64 v); static quint64 gcd(quint64 u, quint64 v); }; diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h new file mode 100644 index 0000000..e6023ec --- /dev/null +++ b/common/abstractprotocolconfig.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2013-2014 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 +*/ +#ifndef _ABSTRACT_PROTOCOL_CONFIG_H +#define _ABSTRACT_PROTOCOL_CONFIG_H + +#include + +class AbstractProtocol; + +class AbstractProtocolConfigForm : public QWidget +{ + Q_OBJECT +public: +/*! + Constructs the widget +*/ + AbstractProtocolConfigForm(QWidget *parent = 0) + : QWidget(parent) + { + // Do nothing! + } + +/*! + Destroys the widget +*/ + virtual ~AbstractProtocolConfigForm() + { + // Do nothing! + } + +/*! + Allocates and returns a new instance of the widget. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ + static AbstractProtocolConfigForm* createInstance() + { + return NULL; + } + +/*! + Loads data from the protocol using it's fieldData() method into this + widget + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void loadWidget(AbstractProtocol *proto) + { + // Do nothing! + } + +/*! + Stores data from this widget into the protocol using the protocol's + setFieldData() method + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void storeWidget(AbstractProtocol *proto) + { + // Do nothing! + } +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index f0563fc..d20c300 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -1,32 +1,12 @@ TEMPLATE = lib CONFIG += qt staticlib -QT += network script xml -INCLUDEPATH += "../extra/qhexedit2/src" +QT -= gui +QT += network script LIBS += \ -lprotobuf -FORMS += \ - pcapfileimport.ui \ - mac.ui \ - payload.ui \ - eth2.ui \ - dot3.ui \ - llc.ui \ - snap.ui \ - vlan.ui \ - arp.ui \ - ip4.ui \ - ip6.ui \ - icmp.ui \ - gmp.ui \ - tcp.ui \ - udp.ui \ - textproto.ui \ - userscript.ui \ - hexdump.ui \ - sample.ui -PROTOS += \ + +PROTOS = \ protocol.proto \ - fileformat.proto \ mac.proto \ payload.proto \ eth2.proto \ @@ -55,24 +35,22 @@ PROTOS += \ userscript.proto \ hexdump.proto \ sample.proto -HEADERS += \ - ostprotolib.h \ + +HEADERS = \ abstractprotocol.h \ comboprotocol.h \ - abstractfileformat.h \ - fileformat.h \ - pcapfileformat.h \ - pdmlfileformat.h \ - pdmlprotocol.h \ - pdmlprotocols.h \ - pdmlreader.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ streambase.h \ + +HEADERS += \ mac.h \ payload.h \ eth2.h \ + ip6.h + +HEADERS1 += \ dot3.h \ llc.h \ snap.h \ @@ -83,7 +61,6 @@ HEADERS += \ vlanstack.h \ arp.h \ ip4.h \ - ip6.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ ip6over4.h \ @@ -100,24 +77,22 @@ HEADERS += \ userscript.h \ hexdump.h \ sample.h -SOURCES += \ - ostprotolib.cpp \ + +SOURCES = \ abstractprotocol.cpp \ crc32c.cpp \ - abstractfileformat.cpp \ - fileformat.cpp \ - pcapfileformat.cpp \ - pdmlfileformat.cpp \ - pdmlprotocol.cpp \ - pdmlprotocols.cpp \ - pdmlreader.cpp \ protocolmanager.cpp \ protocollist.cpp \ protocollistiterator.cpp \ streambase.cpp \ + +SOURCES += \ mac.cpp \ payload.cpp \ eth2.cpp \ + ip6.cpp + +SOURCES1 += \ dot3.cpp \ llc.cpp \ snap.cpp \ @@ -125,7 +100,6 @@ SOURCES += \ svlan.cpp \ arp.cpp \ ip4.cpp \ - ip6.cpp \ icmp.cpp \ gmp.cpp \ igmp.cpp \ @@ -140,3 +114,4 @@ SOURCES += \ QMAKE_DISTCLEAN += object_script.* include(../protobuf.pri) + diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro new file mode 100644 index 0000000..86b39a5 --- /dev/null +++ b/common/ostprotogui.pro @@ -0,0 +1,73 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network xml +LIBS += \ + -lprotobuf + +FORMS = \ + pcapfileimport.ui \ + +FORMS += \ + mac.ui \ + payload.ui \ + eth2.ui \ + ip6.ui \ + +FORMS1 += \ + dot3.ui \ + llc.ui \ + snap.ui \ + vlan.ui \ + arp.ui \ + ip4.ui \ + icmp.ui \ + gmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + userscript.ui \ + hexdump.ui \ + sample.ui + +PROTOS = \ + fileformat.proto + +# TODO: Move fileformat related stuff into a different library +HEADERS = \ + ostprotolib.h \ + abstractfileformat.h \ + fileformat.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h + +HEADERS += \ + abstractprotocolconfig.h \ + protocolwidgetfactory.h \ + macconfig.h \ + payloadconfig.h \ + eth2config.h \ + ip6config.h + +SOURCES += \ + ostprotolib.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + +SOURCES += \ + protocolwidgetfactory.cpp \ + macconfig.cpp \ + payloadconfig.cpp \ + eth2config.cpp \ + ip6config.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp index e46dc8c..bd1fcc2 100644 --- a/common/ostprotolib.cpp +++ b/common/ostprotolib.cpp @@ -24,6 +24,7 @@ QString OstProtoLib::gzipPath_; QString OstProtoLib::diffPath_; QString OstProtoLib::awkPath_; +// TODO: one set method for each external app void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, QString gzipPath, QString diffPath, QString awkPath) { @@ -52,4 +53,3 @@ QString OstProtoLib::awkPath() { return awkPath_; } - diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index e91f270..e1b0225 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -21,9 +21,7 @@ along with this program. If not, see #include "abstractprotocol.h" #include "protocol.pb.h" -#include "mac.h" -#include "payload.h" -#include "eth2.h" +#if 0 #include "dot3.h" #include "llc.h" #include "snap.h" @@ -33,7 +31,6 @@ along with this program. If not, see #include "vlanstack.h" #include "arp.h" #include "ip4.h" -#include "ip6.h" #include "ip6over4.h" #include "ip4over6.h" #include "ip4over4.h" @@ -47,6 +44,12 @@ along with this program. If not, see #include "userscript.h" #include "hexdump.h" #include "sample.h" +#else +#include "mac.h" +#include "payload.h" +#include "eth2.h" +#include "ip6.h" +#endif ProtocolManager *OstProtocolManager; @@ -55,11 +58,7 @@ ProtocolManager::ProtocolManager() /*! \todo (LOW) calls to registerProtocol() should be done by the protocols themselves (once this is done remove the #includes for all the protocols) */ - registerProtocol(OstProto::Protocol::kMacFieldNumber, - (void*) MacProtocol::createInstance); - - registerProtocol(OstProto::Protocol::kEth2FieldNumber, - (void*) Eth2Protocol::createInstance); +#if 0 registerProtocol(OstProto::Protocol::kDot3FieldNumber, (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, @@ -82,8 +81,6 @@ ProtocolManager::ProtocolManager() (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); - registerProtocol(OstProto::Protocol::kIp6FieldNumber, - (void*) Ip6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, (void*) Ip6over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, @@ -108,14 +105,22 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, (void*) SampleProtocol::createInstance); +#else + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); +#endif populateNeighbourProtocols(); } @@ -178,7 +183,9 @@ AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) factory.value(protoNumber); - Q_ASSERT(pc != NULL); + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + numberToNameMap.value(protoNumber).toAscii().constData()); p = (*pc)(stream, parent); diff --git a/common/protocolmanager.h b/common/protocolmanager.h index 07b0604..ff7279b 100644 --- a/common/protocolmanager.h +++ b/common/protocolmanager.h @@ -40,6 +40,7 @@ public: ProtocolManager(); ~ProtocolManager(); + // TODO: make registerProtocol static void registerProtocol(int protoNumber, void *protoInstanceCreator); bool isRegisteredProtocol(int protoNumber); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp new file mode 100644 index 0000000..b25c876 --- /dev/null +++ b/common/protocolwidgetfactory.cpp @@ -0,0 +1,86 @@ +/* +Copyright (C) 2014 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 "protocolwidgetfactory.h" + +#include "macconfig.h" +#include "payloadconfig.h" +#include "eth2config.h" +#include "ip6config.h" + +ProtocolWidgetFactory *OstProtocolWidgetFactory; +QMap ProtocolWidgetFactory::configWidgetFactory; + +ProtocolWidgetFactory::ProtocolWidgetFactory() +{ + /*! + * Ideally Protocol Widgets should register themselves + * with the Factory + */ + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMacFieldNumber, + (void*) MacConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6ConfigForm::createInstance); +} + +ProtocolWidgetFactory::~ProtocolWidgetFactory() +{ + configWidgetFactory.clear(); +} + +void ProtocolWidgetFactory::registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator) +{ + Q_ASSERT(!configWidgetFactory.contains(protoNumber)); + + configWidgetFactory.insert(protoNumber, protoConfigWidgetInstanceCreator); +} + +AbstractProtocolConfigForm* ProtocolWidgetFactory::createConfigWidget( + int protoNumber) +{ + AbstractProtocolConfigForm* (*pc)(); + AbstractProtocolConfigForm* p; + + pc = (AbstractProtocolConfigForm* (*)()) + configWidgetFactory.value(protoNumber); + + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + QString(protoNumber).toAscii().constData()); + + p = (*pc)(); + + return p; +} + +void ProtocolWidgetFactory::deleteConfigWidget( + AbstractProtocolConfigForm *configWidget) +{ + delete configWidget; +} diff --git a/common/protocolwidgetfactory.h b/common/protocolwidgetfactory.h new file mode 100644 index 0000000..ebb69ec --- /dev/null +++ b/common/protocolwidgetfactory.h @@ -0,0 +1,46 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _PROTOCOL_WIDGET_FACTORY_H +#define _PROTOCOL_WIDGET_FACTORY_H + +#include + +class AbstractProtocolConfigForm; + +// Singleton class +class ProtocolWidgetFactory +{ + static QMap configWidgetFactory; + +public: + ProtocolWidgetFactory(); + ~ProtocolWidgetFactory(); + + // TODO: make registerProtocolConfigWidget static?? + // TODO: define a function pointer prototype instead of void* for + // protoConfigWidgetInstanceCreator + static void registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator); + + AbstractProtocolConfigForm* createConfigWidget(int protoNumber); + void deleteConfigWidget(AbstractProtocolConfigForm *configWidget); +}; + +#endif diff --git a/ost.pro b/ost.pro index 0f9d987..9e8bbea 100644 --- a/ost.pro +++ b/ost.pro @@ -4,5 +4,6 @@ SUBDIRS = \ extra \ rpc/pbrpc.pro \ common/ostproto.pro \ + common/ostprotogui.pro \ server/drone.pro \ client/ostinato.pro From 37ca28ca94314f93f6f5f0efd1cb1584d09837c2 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 13 Mar 2014 06:08:43 +0530 Subject: [PATCH 211/294] NOX: Mac - Separated protocol and widget as per new framework --- common/mac.cpp | 216 +++++++++++++++++++++++-------------------- common/mac.h | 24 +---- common/macconfig.cpp | 148 +++++++++++++++++++++++++++++ common/macconfig.h | 45 +++++++++ 4 files changed, 312 insertions(+), 121 deletions(-) create mode 100644 common/macconfig.cpp create mode 100644 common/macconfig.h diff --git a/common/mac.cpp b/common/mac.cpp index 788a10d..ec2bcc6 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -17,66 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "mac.h" -MacConfigForm::MacConfigForm(QWidget *parent) - : QWidget(parent) -{ - QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); - - setupUi(this); - leDstMac->setValidator(new QRegExpValidator(reMac, this)); - leSrcMac->setValidator(new QRegExpValidator(reMac, this)); - leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); - leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); -} - -MacConfigForm::~MacConfigForm() -{ - qDebug("In MacConfigForm destructor"); -} - -void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) -{ - if (index == OstProto::Mac::e_mm_fixed) - { - leDstMacCount->setEnabled(false); - leDstMacStep->setEnabled(false); - } - else - { - leDstMacCount->setEnabled(true); - leDstMacStep->setEnabled(true); - } -} - -void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) -{ - if (index == OstProto::Mac::e_mm_fixed) - { - leSrcMacCount->setEnabled(false); - leSrcMacStep->setEnabled(false); - } - else - { - leSrcMacCount->setEnabled(true); - leSrcMacStep->setEnabled(true); - } -} - - MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } MacProtocol::~MacProtocol() { - delete configForm; } AbstractProtocol* MacProtocol::createInstance(StreamBase *stream @@ -239,13 +188,50 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, } break; } + // Meta fields case mac_dstMacMode: + switch(attrib) + { + case FieldValue: return data.dst_mac_mode(); + default: break; + } + break; case mac_dstMacCount: + switch(attrib) + { + case FieldValue: return data.dst_mac_count(); + default: break; + } + break; case mac_dstMacStep: + switch(attrib) + { + case FieldValue: return data.dst_mac_step(); + default: break; + } + break; case mac_srcMacMode: + switch(attrib) + { + case FieldValue: return data.src_mac_mode(); + default: break; + } + break; case mac_srcMacCount: + switch(attrib) + { + case FieldValue: return data.src_mac_count(); + default: break; + } + break; case mac_srcMacStep: + switch(attrib) + { + case FieldValue: return data.src_mac_step(); + default: break; + } + break; default: break; } @@ -253,10 +239,86 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool MacProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, - FieldAttrib /*attrib*/) +bool MacProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case mac_dstAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_dst_mac(mac); + break; + } + case mac_srcAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_src_mac(mac); + break; + } + + // Meta-Fields + case mac_dstMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_dstMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_count(count); + break; + } + case mac_dstMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_step(step); + break; + } + case mac_srcMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_srcMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_count(count); + break; + } + case mac_srcMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_step(step); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; } bool MacProtocol::isProtocolFrameValueVariable() const @@ -281,49 +343,3 @@ int MacProtocol::protocolFrameVariableCount() const return count; } -QWidget* MacProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new MacConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void MacProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->leDstMac->setText(uintToHexStr(data.dst_mac(), 6)); - configForm->cmbDstMacMode->setCurrentIndex(data.dst_mac_mode()); - configForm->leDstMacCount->setText(QString().setNum(data.dst_mac_count())); - configForm->leDstMacStep->setText(QString().setNum(data.dst_mac_step())); - - configForm->leSrcMac->setText(uintToHexStr(data.src_mac(), 6)); - configForm->cmbSrcMacMode->setCurrentIndex(data.src_mac_mode()); - configForm->leSrcMacCount->setText(QString().setNum(data.src_mac_count())); - configForm->leSrcMacStep->setText(QString().setNum(data.src_mac_step())); -} - -void MacProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - data.set_dst_mac(configForm->leDstMac->text().remove(QChar(' ')). - toULongLong(&isOk, 16)); - data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) configForm-> - cmbDstMacMode->currentIndex()); - data.set_dst_mac_count(configForm->leDstMacCount->text().toULong(&isOk)); - data.set_dst_mac_step(configForm->leDstMacStep->text().toULong(&isOk)); - - data.set_src_mac(configForm->leSrcMac->text().remove(QChar(' ')). - toULongLong(&isOk, 16)); - data.set_src_mac_mode((OstProto::Mac::MacAddrMode) configForm-> - cmbSrcMacMode->currentIndex()); - data.set_src_mac_count(configForm->leSrcMacCount->text().toULong(&isOk)); - data.set_src_mac_step(configForm->leSrcMacStep->text().toULong(&isOk)); -} - diff --git a/common/mac.h b/common/mac.h index 32cd7e2..85e0ad5 100644 --- a/common/mac.h +++ b/common/mac.h @@ -23,26 +23,10 @@ along with this program. If not, see #include "abstractprotocol.h" #include "mac.pb.h" -#include "ui_mac.h" - -#define MAX_MAC_ITER_COUNT 256 - -class MacConfigForm : public QWidget, public Ui::mac -{ - Q_OBJECT -public: - MacConfigForm(QWidget *parent = 0); - virtual ~MacConfigForm(); -private slots: - void on_cmbDstMacMode_currentIndexChanged(int index); - void on_cmbSrcMacMode_currentIndexChanged(int index); -}; class MacProtocol : public AbstractProtocol { -private: - OstProto::Mac data; - MacConfigForm *configForm; +public: enum macfield { mac_dstAddr = 0, @@ -58,7 +42,6 @@ private: mac_fieldCount }; -public: MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~MacProtocol(); @@ -83,9 +66,8 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Mac data; }; #endif diff --git a/common/macconfig.cpp b/common/macconfig.cpp new file mode 100644 index 0000000..f17c140 --- /dev/null +++ b/common/macconfig.cpp @@ -0,0 +1,148 @@ +/* +Copyright (C) 2010,2013-2014 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 "macconfig.h" +#include "mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +MacConfigForm::MacConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ +} + +MacConfigForm* MacConfigForm::createInstance() +{ + MacConfigForm *f = new MacConfigForm; + return f; +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + +void MacConfigForm::loadWidget(AbstractProtocol *proto) +{ + leDstMac->setText( + proto->fieldData( + MacProtocol::mac_dstAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbDstMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_dstMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leDstMacCount->setText( + proto->fieldData( + MacProtocol::mac_dstMacCount, + AbstractProtocol::FieldValue + ).toString()); + leDstMacStep->setText( + proto->fieldData( + MacProtocol::mac_dstMacStep, + AbstractProtocol::FieldValue + ).toString()); + + leSrcMac->setText( + proto->fieldData( + MacProtocol::mac_srcAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbSrcMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_srcMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leSrcMacCount->setText( + proto->fieldData( + MacProtocol::mac_srcMacCount, + AbstractProtocol::FieldValue + ).toString()); + leSrcMacStep->setText( + proto->fieldData( + MacProtocol::mac_srcMacStep, + AbstractProtocol::FieldValue + ).toString()); +} + +void MacConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + MacProtocol::mac_dstAddr, + leDstMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_dstMacMode, + cmbDstMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_dstMacCount, + leDstMacCount->text()); + proto->setFieldData( + MacProtocol::mac_dstMacStep, + leDstMacStep->text()); + + proto->setFieldData( + MacProtocol::mac_srcAddr, + leSrcMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_srcMacMode, + cmbSrcMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_srcMacCount, + leSrcMacCount->text()); + proto->setFieldData( + MacProtocol::mac_srcMacStep, + leSrcMacStep->text()); +} + diff --git a/common/macconfig.h b/common/macconfig.h new file mode 100644 index 0000000..952c64e --- /dev/null +++ b/common/macconfig.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _MAC_CONFIG_H +#define _MAC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_mac.h" + +class MacConfigForm : + public AbstractProtocolConfigForm, + private Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); + + static MacConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +#endif From c0f009c44a7c2d84b1a71b5ae7c7499dcf8f367c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 13 Mar 2014 06:29:56 +0530 Subject: [PATCH 212/294] NOX: Eth2 - Separated protocol and widget as per new framework --- common/eth2.cpp | 43 ----------------------------- common/eth2.h | 18 +++---------- common/eth2config.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++ common/eth2config.h | 41 ++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 58 deletions(-) create mode 100644 common/eth2config.cpp create mode 100644 common/eth2config.h diff --git a/common/eth2.cpp b/common/eth2.cpp index 73038b7..63f361a 100644 --- a/common/eth2.cpp +++ b/common/eth2.cpp @@ -17,26 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "eth2.h" -Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } Eth2Protocol::~Eth2Protocol() { - delete configForm; } AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, @@ -197,35 +186,3 @@ bool Eth2Protocol::setFieldData(int index, const QVariant &value, } return isOk; } - -QWidget* Eth2Protocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new Eth2ConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void Eth2Protocol::loadConfigWidget() -{ - configWidget(); - - configForm->cbOverrideType->setChecked( - fieldData(eth2_is_override_type, FieldValue).toBool()); - configForm->leType->setText(uintToHexStr( - fieldData(eth2_type, FieldValue).toUInt(), 2)); -} - -void Eth2Protocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(eth2_is_override_type, - configForm->cbOverrideType->isChecked()); - data.set_type(configForm->leType->text().remove(QChar(' ')).toULong(&isOk, 16)); -} - diff --git a/common/eth2.h b/common/eth2.h index 5694339..5846d14 100644 --- a/common/eth2.h +++ b/common/eth2.h @@ -23,20 +23,11 @@ along with this program. If not, see #include "abstractprotocol.h" #include "eth2.pb.h" -#include "ui_eth2.h" -class Eth2ConfigForm : public QWidget, public Ui::eth2 -{ - Q_OBJECT -public: - Eth2ConfigForm(QWidget *parent = 0); -}; class Eth2Protocol : public AbstractProtocol { -private: - OstProto::Eth2 data; - Eth2ConfigForm *configForm; +public: enum eth2field { eth2_type = 0, @@ -46,7 +37,6 @@ private: eth2_fieldCount }; -public: Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Eth2Protocol(); @@ -69,10 +59,8 @@ public: int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Eth2 data; }; #endif diff --git a/common/eth2config.cpp b/common/eth2config.cpp new file mode 100644 index 0000000..7cf3bd5 --- /dev/null +++ b/common/eth2config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010,2014 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 "eth2config.h" +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +Eth2ConfigForm::~Eth2ConfigForm() +{ +} + +Eth2ConfigForm* Eth2ConfigForm::createInstance() +{ + return new Eth2ConfigForm; +} + +void Eth2ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideType->setChecked( + proto->fieldData( + Eth2Protocol::eth2_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + Eth2Protocol::eth2_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void Eth2ConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + Eth2Protocol::eth2_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + Eth2Protocol::eth2_type, + leType->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/eth2config.h b/common/eth2config.h new file mode 100644 index 0000000..918a855 --- /dev/null +++ b/common/eth2config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010,2014 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 +*/ + +#ifndef _ETH2_CONFIG_H +#define _ETH2_CONFIG_H + +#include "abstractprotocolconfig.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : + public AbstractProtocolConfigForm, + public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); + virtual ~Eth2ConfigForm(); + + static Eth2ConfigForm* createInstance(); + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; +#endif From f39031d663fccc4daae0a6f7ef505a7785e229fe Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 13 Mar 2014 07:27:29 +0530 Subject: [PATCH 213/294] NOX: Payload - Separated protocol and widget as per new framework --- common/payload.cpp | 102 +++++++++++++++------------------------ common/payload.h | 22 ++------- common/payloadconfig.cpp | 84 ++++++++++++++++++++++++++++++++ common/payloadconfig.h | 44 +++++++++++++++++ 4 files changed, 170 insertions(+), 82 deletions(-) create mode 100644 common/payloadconfig.cpp create mode 100644 common/payloadconfig.h diff --git a/common/payload.cpp b/common/payload.cpp index 7fd0dd5..2a7756d 100644 --- a/common/payload.cpp +++ b/common/payload.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2013-2014 Srivats P. This file is part of "Ostinato" @@ -17,46 +17,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - -//#include "../client/stream.h" #include "payload.h" #include "streambase.h" - -PayloadConfigForm::PayloadConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - -void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) -{ - switch(index) - { - case OstProto::Payload::e_dp_fixed_word: - lePattern->setEnabled(true); - break; - case OstProto::Payload::e_dp_inc_byte: - case OstProto::Payload::e_dp_dec_byte: - case OstProto::Payload::e_dp_random: - lePattern->setDisabled(true); - break; - default: - qWarning("Unhandled/Unknown PatternMode = %d",index); - } -} - PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } PayloadProtocol::~PayloadProtocol() { - delete configForm; } AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, @@ -196,6 +166,12 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, // Meta fields case payload_dataPatternMode: + switch(attrib) + { + case FieldValue: return data.pattern_mode(); + default: break; + } + break; default: break; } @@ -203,10 +179,38 @@ QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool PayloadProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool PayloadProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case payload_dataPattern: + { + uint pattern = value.toUInt(&isOk); + if (isOk) + data.set_pattern(pattern); + break; + } + case payload_dataPatternMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.DataPatternMode_IsValid(mode)) + data.set_pattern_mode(OstProto::Payload::DataPatternMode(mode)); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; } bool PayloadProtocol::isProtocolFrameValueVariable() const @@ -252,33 +256,3 @@ int PayloadProtocol::protocolFrameVariableCount() const return count; } - -QWidget* PayloadProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new PayloadConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void PayloadProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->cmbPatternMode->setCurrentIndex(data.pattern_mode()); - configForm->lePattern->setText(uintToHexStr(data.pattern(), 4)); -} - -void PayloadProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - data.set_pattern_mode((OstProto::Payload::DataPatternMode) - configForm->cmbPatternMode->currentIndex()); - data.set_pattern(configForm->lePattern->text().remove(QChar(' ')).toULong(&isOk, 16)); -} - diff --git a/common/payload.h b/common/payload.h index 822ee37..4c95da5 100644 --- a/common/payload.h +++ b/common/payload.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" @@ -23,22 +23,10 @@ along with this program. If not, see #include "abstractprotocol.h" #include "payload.pb.h" -#include "ui_payload.h" - -class PayloadConfigForm : public QWidget, public Ui::payload -{ - Q_OBJECT -public: - PayloadConfigForm(QWidget *parent = 0); -private slots: - void on_cmbPatternMode_currentIndexChanged(int index); -}; class PayloadProtocol : public AbstractProtocol { -private: - OstProto::Payload data; - PayloadConfigForm *configForm; +public: enum payloadfield { payload_dataPattern, @@ -49,7 +37,6 @@ private: payload_fieldCount }; -public: PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~PayloadProtocol(); @@ -77,9 +64,8 @@ public: virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Payload data; }; #endif diff --git a/common/payloadconfig.cpp b/common/payloadconfig.cpp new file mode 100644 index 0000000..d06d672 --- /dev/null +++ b/common/payloadconfig.cpp @@ -0,0 +1,84 @@ +/* +Copyright (C) 2010-2014 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 "payloadconfig.h" + +#include "payload.h" + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +PayloadConfigForm::~PayloadConfigForm() +{ +} + +AbstractProtocolConfigForm* PayloadConfigForm::createInstance() +{ + return new PayloadConfigForm; +} + +void PayloadConfigForm::loadWidget(AbstractProtocol *proto) +{ + cmbPatternMode->setCurrentIndex( + proto->fieldData( + PayloadProtocol::payload_dataPatternMode, + AbstractProtocol::FieldValue + ).toUInt()); + lePattern->setText(uintToHexStr( + proto->fieldData( + PayloadProtocol::payload_dataPattern, + AbstractProtocol::FieldValue + ).toUInt(), 4)); +} + +void PayloadConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + PayloadProtocol::payload_dataPatternMode, + cmbPatternMode->currentIndex()); + + proto->setFieldData( + PayloadProtocol::payload_dataPattern, + lePattern->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + + + diff --git a/common/payloadconfig.h b/common/payloadconfig.h new file mode 100644 index 0000000..8ebfeb0 --- /dev/null +++ b/common/payloadconfig.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _PAYLOAD_CONFIG_H +#define _PAYLOAD_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_payload.h" + +class PayloadConfigForm : + public AbstractProtocolConfigForm, + private Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); + virtual ~PayloadConfigForm(); + + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +#endif From bdc1252b95f413271fe173454be67f4a04a372fb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 14 Mar 2014 06:16:46 +0530 Subject: [PATCH 214/294] NOX: IPv6 - Separated protocol and widget as per new framework --- common/ip6.cpp | 147 --------------------------- common/ip6.h | 27 +---- common/ip6config.cpp | 233 +++++++++++++++++++++++++++++++++++++++++++ common/ip6config.h | 44 ++++++++ 4 files changed, 281 insertions(+), 170 deletions(-) create mode 100644 common/ip6config.cpp create mode 100644 common/ip6config.h diff --git a/common/ip6.cpp b/common/ip6.cpp index 266c8c2..79a0868 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -18,66 +18,16 @@ along with this program. If not, see */ #include "ip6.h" - -#include "ipv6addressvalidator.h" - #include -#include -Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - version->setValidator(new QIntValidator(0, 0xF, this)); - payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); - hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); - - srcAddr->setValidator(new IPv6AddressValidator(this)); - srcAddrCount->setValidator(new QIntValidator(this)); - //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); - - dstAddr->setValidator(new IPv6AddressValidator(this)); - dstAddrCount->setValidator(new QIntValidator(this)); - //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); -} - -void Ip6ConfigForm::on_srcAddr_editingFinished() -{ - srcAddr->setText(QHostAddress(srcAddr->text()).toString()); -} - -void Ip6ConfigForm::on_dstAddr_editingFinished() -{ - dstAddr->setText(QHostAddress(dstAddr->text()).toString()); -} - -void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) -{ - bool enabled = (index > 0); - - srcAddrCount->setEnabled(enabled); - srcAddrPrefix->setEnabled(enabled); -} - -void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) -{ - bool enabled = (index > 0); - - dstAddrCount->setEnabled(enabled); - dstAddrPrefix->setEnabled(enabled); -} Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - /* The configWidget is created lazily */ - configForm = NULL; } Ip6Protocol::~Ip6Protocol() { - delete configForm; } AbstractProtocol* Ip6Protocol::createInstance(StreamBase *stream, @@ -854,100 +804,3 @@ quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } -QWidget* Ip6Protocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new Ip6ConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void Ip6Protocol::loadConfigWidget() -{ - configWidget(); - - configForm->isVersionOverride->setChecked( - fieldData(ip6_isOverrideVersion, FieldValue).toBool()); - configForm->version->setText( - fieldData(ip6_version, FieldValue).toString()); - - configForm->trafficClass->setText(uintToHexStr( - fieldData(ip6_trafficClass, FieldValue).toUInt(), 1)); - - configForm->flowLabel->setText(QString("%1").arg( - fieldData(ip6_flowLabel, FieldValue).toUInt(),5, BASE_HEX, QChar('0'))); - - configForm->isPayloadLengthOverride->setChecked( - fieldData(ip6_isOverridePayloadLength, FieldValue).toBool()); - configForm->payloadLength->setText( - fieldData(ip6_payloadLength, FieldValue).toString()); - - configForm->isNextHeaderOverride->setChecked( - fieldData(ip6_isOverrideNextHeader, FieldValue).toBool()); - configForm->nextHeader->setText(uintToHexStr( - fieldData(ip6_nextHeader, FieldValue).toUInt(), 1)); - - configForm->hopLimit->setText( - fieldData(ip6_hopLimit, FieldValue).toString()); - - configForm->srcAddr->setText( - fieldData(ip6_srcAddress, FieldTextValue).toString()); - configForm->srcAddrModeCombo->setCurrentIndex( - fieldData(ip6_srcAddrMode, FieldValue).toUInt()); - configForm->srcAddrCount->setText( - fieldData(ip6_srcAddrCount, FieldValue).toString()); - configForm->srcAddrPrefix->setText( - fieldData(ip6_srcAddrPrefix, FieldValue).toString()); - - configForm->dstAddr->setText( - fieldData(ip6_dstAddress, FieldTextValue).toString()); - configForm->dstAddrModeCombo->setCurrentIndex( - fieldData(ip6_dstAddrMode, FieldValue).toUInt()); - configForm->dstAddrCount->setText( - fieldData(ip6_dstAddrCount, FieldValue).toString()); - configForm->dstAddrPrefix->setText( - fieldData(ip6_dstAddrPrefix, FieldValue).toString()); -} - -void Ip6Protocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(ip6_isOverrideVersion, - configForm->isVersionOverride->isChecked()); - setFieldData(ip6_version, configForm->version->text()); - - setFieldData(ip6_trafficClass, - configForm->trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); - - setFieldData(ip6_flowLabel, - configForm->flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); - - setFieldData(ip6_isOverridePayloadLength, - configForm->isPayloadLengthOverride->isChecked()); - setFieldData(ip6_payloadLength, configForm->payloadLength->text()); - - setFieldData(ip6_isOverrideNextHeader, - configForm->isNextHeaderOverride->isChecked()); - setFieldData(ip6_nextHeader, - configForm->nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); - - setFieldData(ip6_hopLimit, configForm->hopLimit->text()); - - setFieldData(ip6_srcAddress, configForm->srcAddr->text()); - setFieldData(ip6_srcAddrMode, configForm->srcAddrModeCombo->currentIndex()); - setFieldData(ip6_srcAddrCount, configForm->srcAddrCount->text()); - setFieldData(ip6_srcAddrPrefix, configForm->srcAddrPrefix->text()); - - setFieldData(ip6_dstAddress, configForm->dstAddr->text()); - setFieldData(ip6_dstAddrMode, configForm->dstAddrModeCombo->currentIndex()); - setFieldData(ip6_dstAddrCount, configForm->dstAddrCount->text()); - setFieldData(ip6_dstAddrPrefix, configForm->dstAddrPrefix->text()); -} - diff --git a/common/ip6.h b/common/ip6.h index dfaf02d..63f6e7e 100644 --- a/common/ip6.h +++ b/common/ip6.h @@ -20,10 +20,8 @@ along with this program. If not, see #ifndef _IP6_H #define _IP6_H -#include "ip6.pb.h" -#include "ui_ip6.h" - #include "abstractprotocol.h" +#include "ip6.pb.h" /* IPv6 Protocol Frame Format - @@ -47,23 +45,9 @@ IPv6 Protocol Frame Format - Figures in brackets represent field width in bits */ -class Ip6ConfigForm : public QWidget, public Ui::Ip6 -{ - Q_OBJECT -public: - Ip6ConfigForm(QWidget *parent = 0); -private slots: - void on_srcAddr_editingFinished(); - void on_dstAddr_editingFinished(); - void on_srcAddrModeCombo_currentIndexChanged(int index); - void on_dstAddrModeCombo_currentIndexChanged(int index); -}; - class Ip6Protocol : public AbstractProtocol { -private: - OstProto::Ip6 data; - Ip6ConfigForm *configForm; +public: enum ip6field { // Frame Fields @@ -92,7 +76,6 @@ private: ip6_fieldCount }; -public: Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Ip6Protocol(); @@ -122,10 +105,8 @@ public: virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; - - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Ip6 data; }; #endif diff --git a/common/ip6config.cpp b/common/ip6config.cpp new file mode 100644 index 0000000..1ddd20b --- /dev/null +++ b/common/ip6config.cpp @@ -0,0 +1,233 @@ +/* +Copyright (C) 2010-2014 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 "ip6config.h" +#include "ip6.h" +#include "ipv6addressvalidator.h" +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +AbstractProtocolConfigForm* Ip6ConfigForm::createInstance() +{ + return new Ip6ConfigForm; +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::loadWidget(AbstractProtocol *ip6Proto) +{ + isVersionOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideVersion, + AbstractProtocol::FieldValue + ).toBool()); + version->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_version, + AbstractProtocol::FieldValue + ).toString()); + + trafficClass->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_trafficClass, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + flowLabel->setText(QString("%1").arg( + ip6Proto->fieldData( + Ip6Protocol::ip6_flowLabel, + AbstractProtocol::FieldValue + ).toUInt(), 5, BASE_HEX, QChar('0'))); + + isPayloadLengthOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + AbstractProtocol::FieldValue + ).toBool()); + payloadLength->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isNextHeaderOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + AbstractProtocol::FieldValue + ).toBool()); + nextHeader->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_nextHeader, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + hopLimit->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_hopLimit, + AbstractProtocol::FieldValue + ).toString()); + + srcAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddress, + AbstractProtocol::FieldTextValue + ).toString()); + srcAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + srcAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + srcAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); + + dstAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddress, + AbstractProtocol::FieldTextValue + ).toString()); + dstAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + dstAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + dstAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); +} + +void Ip6ConfigForm::storeWidget(AbstractProtocol *ip6Proto) +{ + bool isOk; + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideVersion, + isVersionOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_version, + version->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_trafficClass, + trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_flowLabel, + flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + isPayloadLengthOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_payloadLength, + payloadLength->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + isNextHeaderOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_nextHeader, + nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_hopLimit, + hopLimit->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddress, + srcAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrMode, + srcAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrCount, + srcAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrPrefix, + srcAddrPrefix->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddress, + dstAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrMode, + dstAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrCount, + dstAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrPrefix, + dstAddrPrefix->text()); +} + diff --git a/common/ip6config.h b/common/ip6config.h new file mode 100644 index 0000000..0ea08c1 --- /dev/null +++ b/common/ip6config.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ +#ifndef _IP6_CONFIG_H +#define _IP6_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip6.h" + +class Ip6ConfigForm : + public AbstractProtocolConfigForm, + private Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *ip6Proto); + virtual void storeWidget(AbstractProtocol *ip6Proto); + +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +#endif From e6fa745b81720eb5ccc1754f5a05183ed442fb05 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 14 Mar 2014 07:03:44 +0530 Subject: [PATCH 215/294] NOX: 802.3 - Separated protocol and widget as per new framework --- common/dot3.cpp | 49 ++----------------------- common/dot3.h | 21 ++--------- common/dot3config.cpp | 63 ++++++++++++++++++++++++++++++++ common/dot3config.h | 41 +++++++++++++++++++++ common/ostproto.pro | 4 +- common/ostprotogui.pro | 4 +- common/protocolmanager.cpp | 9 +++-- common/protocolwidgetfactory.cpp | 4 ++ 8 files changed, 125 insertions(+), 70 deletions(-) create mode 100644 common/dot3config.cpp create mode 100644 common/dot3config.h diff --git a/common/dot3.cpp b/common/dot3.cpp index 6b7afb0..68ef9a6 100644 --- a/common/dot3.cpp +++ b/common/dot3.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" @@ -18,28 +18,14 @@ along with this program. If not, see */ #include "dot3.h" -#include "streambase.h" - -#include -#include -#include - -Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - leLength->setValidator(new QIntValidator(0, 16384, this)); -} Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } Dot3Protocol::~Dot3Protocol() { - delete configForm; } AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, @@ -76,7 +62,7 @@ QString Dot3Protocol::shortName() const return QString("802.3"); } -int Dot3Protocol::fieldCount() const +int Dot3Protocol::fieldCount() const { return dot3_fieldCount; } @@ -92,6 +78,7 @@ AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const case dot3_length: break; + // Meta fields case dot3_is_override_length: flags &= ~FrameField; flags |= MetaField; @@ -207,33 +194,3 @@ int Dot3Protocol::protocolFrameVariableCount() const { return protocolFramePayloadVariableCount(); } - -QWidget* Dot3Protocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new Dot3ConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void Dot3Protocol::loadConfigWidget() -{ - configWidget(); - - configForm->cbOverrideLength->setChecked( - fieldData(dot3_is_override_length, FieldValue).toBool()); - configForm->leLength->setText( - fieldData(dot3_length, FieldValue).toString()); -} - -void Dot3Protocol::storeConfigWidget() -{ - configWidget(); - - setFieldData(dot3_is_override_length, - configForm->cbOverrideLength->isChecked()); - setFieldData(dot3_length,configForm->leLength->text()); -} - diff --git a/common/dot3.h b/common/dot3.h index 22c3f5b..ecade93 100644 --- a/common/dot3.h +++ b/common/dot3.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" @@ -21,22 +21,11 @@ along with this program. If not, see #define _DOT3_H #include "abstractprotocol.h" - #include "dot3.pb.h" -#include "ui_dot3.h" - -class Dot3ConfigForm : public QWidget, public Ui::dot3 -{ - Q_OBJECT -public: - Dot3ConfigForm(QWidget *parent = 0); -}; class Dot3Protocol : public AbstractProtocol { -private: - OstProto::Dot3 data; - Dot3ConfigForm *configForm; +public: enum Dot3field { dot3_length, @@ -47,7 +36,6 @@ private: dot3_fieldCount }; -public: Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Dot3Protocol(); @@ -72,9 +60,8 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Dot3 data; }; #endif diff --git a/common/dot3config.cpp b/common/dot3config.cpp new file mode 100644 index 0000000..d17094e --- /dev/null +++ b/common/dot3config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010-2014 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 "dot3config.h" +#include "dot3.h" +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3ConfigForm::~Dot3ConfigForm() +{ +} + +Dot3ConfigForm* Dot3ConfigForm::createInstance() +{ + return new Dot3ConfigForm; +} + +void Dot3ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideLength->setChecked( + proto->fieldData( + Dot3Protocol::dot3_is_override_length, + AbstractProtocol::FieldValue + ).toBool()); + leLength->setText( + proto->fieldData( + Dot3Protocol::dot3_length, + AbstractProtocol::FieldValue + ).toString()); +} + +void Dot3ConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + Dot3Protocol::dot3_is_override_length, + cbOverrideLength->isChecked()); + proto->setFieldData( + Dot3Protocol::dot3_length, + leLength->text()); +} + diff --git a/common/dot3config.h b/common/dot3config.h new file mode 100644 index 0000000..44e3e7e --- /dev/null +++ b/common/dot3config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _DOT3_CONFIG_H +#define _DOT3_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : + public AbstractProtocolConfigForm, + private Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); + virtual ~Dot3ConfigForm(); + + static Dot3ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index d20c300..92863ca 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -47,11 +47,11 @@ HEADERS = \ HEADERS += \ mac.h \ payload.h \ + dot3.h \ eth2.h \ ip6.h HEADERS1 += \ - dot3.h \ llc.h \ snap.h \ dot2llc.h \ @@ -89,11 +89,11 @@ SOURCES = \ SOURCES += \ mac.cpp \ payload.cpp \ + dot3.cpp \ eth2.cpp \ ip6.cpp SOURCES1 += \ - dot3.cpp \ llc.cpp \ snap.cpp \ vlan.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 86b39a5..8f3c4ee 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -10,11 +10,11 @@ FORMS = \ FORMS += \ mac.ui \ payload.ui \ + dot3.ui \ eth2.ui \ ip6.ui \ FORMS1 += \ - dot3.ui \ llc.ui \ snap.ui \ vlan.ui \ @@ -48,6 +48,7 @@ HEADERS += \ protocolwidgetfactory.h \ macconfig.h \ payloadconfig.h \ + dot3config.h \ eth2config.h \ ip6config.h @@ -65,6 +66,7 @@ SOURCES += \ protocolwidgetfactory.cpp \ macconfig.cpp \ payloadconfig.cpp \ + dot3config.cpp \ eth2config.cpp \ ip6config.cpp diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index e1b0225..c8c0bb7 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "dot3.h" #include "llc.h" #include "snap.h" #include "dot2llc.h" @@ -47,6 +46,7 @@ along with this program. If not, see #else #include "mac.h" #include "payload.h" +#include "dot3.h" #include "eth2.h" #include "ip6.h" #endif @@ -59,8 +59,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kDot3FieldNumber, - (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, @@ -116,6 +114,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, @@ -185,7 +185,8 @@ AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, Q_ASSERT_X(pc != NULL, __FUNCTION__, - numberToNameMap.value(protoNumber).toAscii().constData()); + QString("No Protocol Creator registered for protocol %1") + .arg(protoNumber).toAscii().constData()); p = (*pc)(stream, parent); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index b25c876..82a7231 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -21,6 +21,7 @@ along with this program. If not, see #include "macconfig.h" #include "payloadconfig.h" +#include "dot3config.h" #include "eth2config.h" #include "ip6config.h" @@ -40,6 +41,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, (void*) Eth2ConfigForm::createInstance); From 4fa4046e9e8e3c7bd54fea5ae6f55f36e8dcc1eb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 20 Mar 2014 05:57:30 +0530 Subject: [PATCH 216/294] NOX: Temp mask of some streamconfigdialog code till all protocols are changed to new framework --- client/streamconfigdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 087631d..a9b94ea 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -86,7 +86,7 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); -#if 1 // temp mask +#if 0 // temp mask // Setup valid subsequent protocols for L2 to L4 protocols for (int i = ProtoL2; i <= ProtoL4; i++) { From a97a7d9511e0f5435f2557ea7ea84b4577ca7ef7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 20 Mar 2014 06:03:55 +0530 Subject: [PATCH 217/294] NOX: VLAN - Separated protocol and widget as per new framework + cosmetic layout change in the widget --- common/ostproto.pro | 4 +- common/ostprotogui.pro | 4 +- common/protocolmanager.cpp | 9 +- common/protocolwidgetfactory.cpp | 7 + common/vlan.cpp | 105 +++++++----- common/vlan.h | 25 +-- common/vlan.ui | 284 ++++++++++++++++--------------- common/vlanconfig.cpp | 87 ++++++++++ common/vlanconfig.h | 41 +++++ 9 files changed, 353 insertions(+), 213 deletions(-) create mode 100644 common/vlanconfig.cpp create mode 100644 common/vlanconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 92863ca..deb3b12 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -48,6 +48,7 @@ HEADERS += \ mac.h \ payload.h \ dot3.h \ + vlan.h \ eth2.h \ ip6.h @@ -56,7 +57,6 @@ HEADERS1 += \ snap.h \ dot2llc.h \ dot2snap.h \ - vlan.h \ svlan.h \ vlanstack.h \ arp.h \ @@ -89,6 +89,7 @@ SOURCES = \ SOURCES += \ mac.cpp \ payload.cpp \ + vlan.cpp \ dot3.cpp \ eth2.cpp \ ip6.cpp @@ -96,7 +97,6 @@ SOURCES += \ SOURCES1 += \ llc.cpp \ snap.cpp \ - vlan.cpp \ svlan.cpp \ arp.cpp \ ip4.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 8f3c4ee..f72f14f 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -10,6 +10,7 @@ FORMS = \ FORMS += \ mac.ui \ payload.ui \ + vlan.ui \ dot3.ui \ eth2.ui \ ip6.ui \ @@ -17,7 +18,6 @@ FORMS += \ FORMS1 += \ llc.ui \ snap.ui \ - vlan.ui \ arp.ui \ ip4.ui \ icmp.ui \ @@ -48,6 +48,7 @@ HEADERS += \ protocolwidgetfactory.h \ macconfig.h \ payloadconfig.h \ + vlanconfig.h \ dot3config.h \ eth2config.h \ ip6config.h @@ -66,6 +67,7 @@ SOURCES += \ protocolwidgetfactory.cpp \ macconfig.cpp \ payloadconfig.cpp \ + vlanconfig.cpp \ dot3config.cpp \ eth2config.cpp \ ip6config.cpp diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index c8c0bb7..aaeecb2 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -26,7 +26,6 @@ along with this program. If not, see #include "snap.h" #include "dot2llc.h" #include "dot2snap.h" -#include "vlan.h" #include "vlanstack.h" #include "arp.h" #include "ip4.h" @@ -47,6 +46,7 @@ along with this program. If not, see #include "mac.h" #include "payload.h" #include "dot3.h" +#include "vlan.h" #include "eth2.h" #include "ip6.h" #endif @@ -70,8 +70,6 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanProtocol::createInstance); - registerProtocol(OstProto::Protocol::kVlanFieldNumber, - (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackProtocol::createInstance); @@ -116,8 +114,13 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kDot3FieldNumber, (void*) Dot3Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); #endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 82a7231..7a330f1 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -22,6 +22,7 @@ along with this program. If not, see #include "macconfig.h" #include "payloadconfig.h" #include "dot3config.h" +#include "vlanconfig.h" #include "eth2config.h" #include "ip6config.h" @@ -44,9 +45,15 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kDot3FieldNumber, (void*) Dot3ConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanFieldNumber, + (void*) VlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, (void*) Eth2ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, (void*) Ip6ConfigForm::createInstance); diff --git a/common/vlan.cpp b/common/vlan.cpp index 95b2304..cb07cd2 100644 --- a/common/vlan.cpp +++ b/common/vlan.cpp @@ -17,25 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - #include "vlan.h" -VlanConfigForm::VlanConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } VlanProtocol::~VlanProtocol() { - delete configForm; } AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, @@ -206,6 +196,12 @@ QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, // Meta fields case vlan_isOverrideTpid: + switch(attrib) + { + case FieldValue: return data.is_override_tpid(); + default: break; + } + break; default: break; } @@ -213,45 +209,62 @@ QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, return AbstractProtocol::fieldData(index, attrib, streamIndex); } -bool VlanProtocol::setFieldData(int /*index*/, const QVariant &/*value*/, - FieldAttrib /*attrib*/) +bool VlanProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) { - return false; -} + bool isOk = false; + if (attrib != FieldValue) + goto _exit; -QWidget* VlanProtocol::configWidget() -{ - if (configForm == NULL) + switch (index) { - configForm = new VlanConfigForm; - loadConfigWidget(); + case vlan_tpid: + { + uint tpid = value.toUInt(&isOk); + if (isOk) + data.set_tpid(tpid); + break; + } + case vlan_prio: + { + uint prio = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((prio & 0x07) << 13) | (data.vlan_tag() & 0x1FFF)); + break; + } + case vlan_cfiDei: + { + uint cfiDei = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((cfiDei & 0x01) << 12) | (data.vlan_tag() & 0xEFFF)); + break; + } + case vlan_vlanId: + { + uint vlanId = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + (vlanId & 0x0FFF) | (data.vlan_tag() & 0xF000)); + break; + } + + // Meta-Fields + case vlan_isOverrideTpid: + { + bool override = value.toUInt(&isOk); + if (isOk) + data.set_is_override_tpid(override); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; } - return configForm; + +_exit: + return isOk; } - -void VlanProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->cbTpidOverride->setChecked(data.is_override_tpid()); - configForm->leTpid->setText(uintToHexStr(fieldData(vlan_tpid, FieldValue).toUInt(), 2)); - configForm->cmbPrio->setCurrentIndex(fieldData(vlan_prio, FieldValue).toUInt()); - configForm->cmbCfiDei->setCurrentIndex(fieldData(vlan_cfiDei, FieldValue).toUInt()); - configForm->leVlanId->setText(fieldData(vlan_vlanId, FieldValue).toString()); -} - -void VlanProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - data.set_is_override_tpid(configForm->cbTpidOverride->isChecked()); - data.set_tpid(configForm->leTpid->text().remove(QChar(' ')).toULong(&isOk, BASE_HEX)); - data.set_vlan_tag( - ((configForm->cmbPrio->currentIndex() & 0x07) << 13) | - ((configForm->cmbCfiDei->currentIndex() & 0x01) << 12) | - (configForm->leVlanId->text().toULong(&isOk) & 0x0FFF)); -} - diff --git a/common/vlan.h b/common/vlan.h index 728572b..26cf874 100644 --- a/common/vlan.h +++ b/common/vlan.h @@ -17,25 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#ifndef _Vlan_H -#define _Vlan_H +#ifndef _VLAN_H +#define _VLAN_H #include "abstractprotocol.h" - #include "vlan.pb.h" -#include "ui_vlan.h" - -class VlanConfigForm : public QWidget, public Ui::Vlan -{ - Q_OBJECT -public: - VlanConfigForm(QWidget *parent = 0); -}; class VlanProtocol : public AbstractProtocol { -private: - VlanConfigForm *configForm; +public: enum Vlanfield { vlan_tpid, @@ -49,10 +39,6 @@ private: vlan_fieldCount }; -protected: - OstProto::Vlan data; - -public: VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~VlanProtocol(); @@ -74,9 +60,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +protected: + OstProto::Vlan data; }; #endif diff --git a/common/vlan.ui b/common/vlan.ui index 3e0326d..72e49c0 100644 --- a/common/vlan.ui +++ b/common/vlan.ui @@ -5,156 +5,158 @@ 0 0 - 268 - 96 + 274 + 106 Form - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + VLAN - - - - - true - - - Override TPID - - - - - - - Priority - - - - - - - CFI/DEI - - - - - - - VLAN - - - - - - - false - - - >HH HH; - - - - - - - - - - true - - - - 0 - - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 - - - - - - - - true - - - - 0 - - - - - 1 - - - - - - - - true - - - 0 - - - - + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + Qt::Horizontal + + + + 111 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + diff --git a/common/vlanconfig.cpp b/common/vlanconfig.cpp new file mode 100644 index 0000000..35241bd --- /dev/null +++ b/common/vlanconfig.cpp @@ -0,0 +1,87 @@ +/* +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 +*/ + +#include "vlanconfig.h" +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +VlanConfigForm::~VlanConfigForm() +{ +} + +VlanConfigForm* VlanConfigForm::createInstance() +{ + return new VlanConfigForm; +} + +void VlanConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbTpidOverride->setChecked( + proto->fieldData( + VlanProtocol::vlan_isOverrideTpid, + AbstractProtocol::FieldValue + ).toBool()); + leTpid->setText(uintToHexStr( + proto->fieldData( + VlanProtocol::vlan_tpid, + AbstractProtocol::FieldValue) + .toUInt(), 2)); + cmbPrio->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_prio, + AbstractProtocol::FieldValue) + .toUInt()); + cmbCfiDei->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_cfiDei, + AbstractProtocol::FieldValue) + .toUInt()); + leVlanId->setText( + proto->fieldData( + VlanProtocol::vlan_vlanId, + AbstractProtocol::FieldValue) + .toString()); +} + +void VlanConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + VlanProtocol::vlan_isOverrideTpid, + cbTpidOverride->isChecked()); + proto->setFieldData( + VlanProtocol::vlan_tpid, + leTpid->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + proto->setFieldData( + VlanProtocol::vlan_prio, + cmbPrio->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_cfiDei, + cmbCfiDei->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_vlanId, + leVlanId->text()); +} + diff --git a/common/vlanconfig.h b/common/vlanconfig.h new file mode 100644 index 0000000..a761873 --- /dev/null +++ b/common/vlanconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _VLAN_CONFIG_H +#define _VLAN_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_vlan.h" + +class VlanConfigForm : + public AbstractProtocolConfigForm, + private Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); + virtual ~VlanConfigForm(); + + static VlanConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif From e1cfd397869b4f4647af468212db71119030c254 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 20 Mar 2014 06:35:40 +0530 Subject: [PATCH 218/294] NOX: SVLAN - Separated protocol and widget as per new framework --- common/ostproto.pro | 4 ++-- common/ostprotogui.pro | 1 + common/protocolmanager.cpp | 7 ++++--- common/protocolwidgetfactory.cpp | 4 ++++ common/svlan.cpp | 2 -- common/svlanconfig.h | 28 ++++++++++++++++++++++++++++ 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 common/svlanconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index deb3b12..331ebb2 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -49,6 +49,7 @@ HEADERS += \ payload.h \ dot3.h \ vlan.h \ + svlan.h \ eth2.h \ ip6.h @@ -57,7 +58,6 @@ HEADERS1 += \ snap.h \ dot2llc.h \ dot2snap.h \ - svlan.h \ vlanstack.h \ arp.h \ ip4.h \ @@ -90,6 +90,7 @@ SOURCES += \ mac.cpp \ payload.cpp \ vlan.cpp \ + svlan.cpp \ dot3.cpp \ eth2.cpp \ ip6.cpp @@ -97,7 +98,6 @@ SOURCES += \ SOURCES1 += \ llc.cpp \ snap.cpp \ - svlan.cpp \ arp.cpp \ ip4.cpp \ icmp.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index f72f14f..b922dfe 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -49,6 +49,7 @@ HEADERS += \ macconfig.h \ payloadconfig.h \ vlanconfig.h \ + svlanconfig.h \ dot3config.h \ eth2config.h \ ip6config.h diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index aaeecb2..6a2f4de 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -46,7 +46,8 @@ along with this program. If not, see #include "mac.h" #include "payload.h" #include "dot3.h" -#include "vlan.h" +#include "vlan.h" +#include "svlan.h" #include "eth2.h" #include "ip6.h" #endif @@ -68,8 +69,6 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kSvlanFieldNumber, - (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackProtocol::createInstance); @@ -117,6 +116,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kVlanFieldNumber, (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 7a330f1..6203dc8 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -23,6 +23,7 @@ along with this program. If not, see #include "payloadconfig.h" #include "dot3config.h" #include "vlanconfig.h" +#include "svlanconfig.h" #include "eth2config.h" #include "ip6config.h" @@ -49,6 +50,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kVlanFieldNumber, (void*) VlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, diff --git a/common/svlan.cpp b/common/svlan.cpp index 893671d..9b5f65b 100644 --- a/common/svlan.cpp +++ b/common/svlan.cpp @@ -17,8 +17,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - #include "svlan.h" #include "svlan.pb.h" diff --git a/common/svlanconfig.h b/common/svlanconfig.h new file mode 100644 index 0000000..e5bd578 --- /dev/null +++ b/common/svlanconfig.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SVLAN_CONFIG_H +#define _SVLAN_CONFIG_H + +#include "vlanconfig.h" + +typedef VlanConfigForm SVlanConfigForm; + +#endif + From 70937bd4a4c01a88d0b210f3b0241c5c3367c1c1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 25 Mar 2014 05:32:27 +0530 Subject: [PATCH 219/294] NOX: ComboProtocol - Separated protocol and widget as per framework; VlanStack - separated protocol and widget using the refactored ComboProtocol --- common/comboprotocol.h | 35 +---------- common/comboprotocolconfig.h | 100 +++++++++++++++++++++++++++++++ common/ostproto.pro | 2 +- common/ostprotogui.pro | 2 + common/protocolmanager.cpp | 7 +-- common/protocolwidgetfactory.cpp | 4 ++ common/vlanstackconfig.h | 38 ++++++++++++ 7 files changed, 149 insertions(+), 39 deletions(-) create mode 100644 common/comboprotocolconfig.h create mode 100644 common/vlanstackconfig.h diff --git a/common/comboprotocol.h b/common/comboprotocol.h index bc148d3..97fe960 100644 --- a/common/comboprotocol.h +++ b/common/comboprotocol.h @@ -28,7 +28,6 @@ class ComboProtocol : public AbstractProtocol protected: ProtoA *protoA; ProtoB *protoB; - QWidget *configForm; public: ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) @@ -38,7 +37,6 @@ public: protoB = new ProtoB(stream, this); protoA->next = protoB; protoB->prev = protoA; - configForm = NULL; qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, protoNumber, protoA, protoB); @@ -46,12 +44,6 @@ public: virtual ~ComboProtocol() { - if (configForm) - { - protoA->configWidget()->setParent(0); - protoB->configWidget()->setParent(0); - delete configForm; - } delete protoA; delete protoB; } @@ -192,32 +184,7 @@ public: quint32 protocolFramePayloadCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; #endif - - virtual QWidget* configWidget() - { - if (configForm == NULL) - { - QVBoxLayout *layout = new QVBoxLayout; - - configForm = new QWidget; - layout->addWidget(protoA->configWidget()); - layout->addWidget(protoB->configWidget()); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - configForm->setLayout(layout); - } - return configForm; - } - virtual void loadConfigWidget() - { - protoA->loadConfigWidget(); - protoB->loadConfigWidget(); - } - virtual void storeConfigWidget() - { - protoA->storeConfigWidget(); - protoB->storeConfigWidget(); - } + template friend class ComboProtocolConfigForm; }; #endif diff --git a/common/comboprotocolconfig.h b/common/comboprotocolconfig.h new file mode 100644 index 0000000..d77627f --- /dev/null +++ b/common/comboprotocolconfig.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_CONFIG_H +#define _COMBO_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "comboprotocol.h" + +template +class ComboProtocolConfigForm : + public AbstractProtocolConfigForm +{ +public: + ComboProtocolConfigForm(QWidget *parent = 0) + : AbstractProtocolConfigForm(parent) + { + QVBoxLayout *layout = new QVBoxLayout; + + formA = new FormA(this); + formB = new FormB(this); + + layout->addWidget(formA); + layout->addWidget(formB); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, formA, formB); + } + + virtual ~ComboProtocolConfigForm() + { + formA->setParent(0); + formB->setParent(0); + + delete formA; + delete formB; + } + + static ComboProtocolConfigForm* createInstance() + { + return new ComboProtocolConfigForm; + } + + virtual void loadWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->loadWidget(comboProto->protoA); + formB->loadWidget(comboProto->protoB); + } + virtual void storeWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->storeWidget(comboProto->protoA); + formB->storeWidget(comboProto->protoB); + } + +protected: + FormA *formA; + FormB *formB; +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 331ebb2..bbc6758 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -50,6 +50,7 @@ HEADERS += \ dot3.h \ vlan.h \ svlan.h \ + vlanstack.h \ eth2.h \ ip6.h @@ -58,7 +59,6 @@ HEADERS1 += \ snap.h \ dot2llc.h \ dot2snap.h \ - vlanstack.h \ arp.h \ ip4.h \ ipv4addressdelegate.h \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index b922dfe..4ecf33e 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -45,11 +45,13 @@ HEADERS = \ HEADERS += \ abstractprotocolconfig.h \ + comboprotocolconfig.h \ protocolwidgetfactory.h \ macconfig.h \ payloadconfig.h \ vlanconfig.h \ svlanconfig.h \ + vlanstackconfig.h \ dot3config.h \ eth2config.h \ ip6config.h diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 6a2f4de..05297e3 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -26,7 +26,6 @@ along with this program. If not, see #include "snap.h" #include "dot2llc.h" #include "dot2snap.h" -#include "vlanstack.h" #include "arp.h" #include "ip4.h" #include "ip6over4.h" @@ -48,6 +47,7 @@ along with this program. If not, see #include "dot3.h" #include "vlan.h" #include "svlan.h" +#include "vlanstack.h" #include "eth2.h" #include "ip6.h" #endif @@ -69,9 +69,6 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, - (void*) VlanStackProtocol::createInstance); - registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, @@ -118,6 +115,8 @@ ProtocolManager::ProtocolManager() (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 6203dc8..1f3e053 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -24,6 +24,7 @@ along with this program. If not, see #include "dot3config.h" #include "vlanconfig.h" #include "svlanconfig.h" +#include "vlanstackconfig.h" #include "eth2config.h" #include "ip6config.h" @@ -53,6 +54,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, diff --git a/common/vlanstackconfig.h b/common/vlanstackconfig.h new file mode 100644 index 0000000..5203824 --- /dev/null +++ b/common/vlanstackconfig.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _VLAN_STACK_CONFIG_H +#define _VLAN_STACK_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "svlanconfig.h" +#include "vlanconfig.h" +#include "svlan.h" +#include "vlan.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kVlanStackFieldNumber, + SVlanConfigForm, VlanConfigForm, + SVlanProtocol, VlanProtocol + > VlanStackConfigForm; + +#endif From a8dcc42d4c0d12436eb4e5eda1f6f1bc76b273bf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 25 Mar 2014 06:13:23 +0530 Subject: [PATCH 220/294] NOX: LLC - Separated protocol and widget as per new framework --- common/llc.cpp | 67 --------------------- common/llc.h | 21 +------ common/llcconfig.cpp | 100 +++++++++++++++++++++++++++++++ common/llcconfig.h | 41 +++++++++++++ common/ostproto.pro | 8 +-- common/ostprotogui.pro | 10 ++-- common/protocolmanager.cpp | 13 ++-- common/protocolwidgetfactory.cpp | 13 ++-- 8 files changed, 168 insertions(+), 105 deletions(-) create mode 100644 common/llcconfig.cpp create mode 100644 common/llcconfig.h diff --git a/common/llc.cpp b/common/llc.cpp index 5adecf1..67b370d 100644 --- a/common/llc.cpp +++ b/common/llc.cpp @@ -17,26 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "llc.h" -LlcConfigForm::LlcConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } LlcProtocol::~LlcProtocol() { - delete configForm; } AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, @@ -273,59 +262,3 @@ bool LlcProtocol::setFieldData(int index, const QVariant &value, } return isOk; } - - -QWidget* LlcProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new LlcConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void LlcProtocol::loadConfigWidget() -{ -#define uintToHexStr(num, bytes) \ - QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) - - configWidget(); - - configForm->cbOverrideDsap->setChecked( - fieldData(llc_is_override_dsap, FieldValue).toBool()); - configForm->leDsap->setText(uintToHexStr( - fieldData(llc_dsap, FieldValue).toUInt(), 1)); - - configForm->cbOverrideSsap->setChecked( - fieldData(llc_is_override_ssap, FieldValue).toBool()); - configForm->leSsap->setText(uintToHexStr( - fieldData(llc_ssap, FieldValue).toUInt(), 1)); - - configForm->cbOverrideControl->setChecked( - fieldData(llc_is_override_ctl, FieldValue).toBool()); - configForm->leControl->setText(uintToHexStr( - fieldData(llc_ctl, FieldValue).toUInt(), 1)); -#undef uintToHexStr -} - -void LlcProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(llc_is_override_dsap, - configForm->cbOverrideDsap->isChecked()); - setFieldData(llc_dsap, configForm->leDsap->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(llc_is_override_ssap, - configForm->cbOverrideSsap->isChecked()); - setFieldData(llc_ssap, configForm->leSsap->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(llc_is_override_ctl, - configForm->cbOverrideControl->isChecked()); - setFieldData(llc_ctl, - configForm->leControl->text().toUInt(&isOk, BASE_HEX)); -} - diff --git a/common/llc.h b/common/llc.h index 808d442..a723177 100644 --- a/common/llc.h +++ b/common/llc.h @@ -20,26 +20,13 @@ along with this program. If not, see #ifndef _LLC_H #define _LLC_H -#include -#include - #include "abstractprotocol.h" #include "llc.pb.h" -#include "ui_llc.h" - -class LlcConfigForm : public QWidget, public Ui::llc -{ - Q_OBJECT -public: - LlcConfigForm(QWidget *parent = 0); -}; class LlcProtocol : public AbstractProtocol { -private: - OstProto::Llc data; - LlcConfigForm *configForm; +public: enum llcfield { llc_dsap = 0, @@ -54,7 +41,6 @@ private: llc_fieldCount }; -public: LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~LlcProtocol(); @@ -78,9 +64,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Llc data; }; #endif diff --git a/common/llcconfig.cpp b/common/llcconfig.cpp new file mode 100644 index 0000000..6ba785c --- /dev/null +++ b/common/llcconfig.cpp @@ -0,0 +1,100 @@ +/* +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 +*/ + +#include "llcconfig.h" +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +LlcConfigForm::~LlcConfigForm() +{ +} + +LlcConfigForm* LlcConfigForm::createInstance() +{ + return new LlcConfigForm; +} + +void LlcConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideDsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_dsap, + AbstractProtocol::FieldValue + ).toBool()); + leDsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_dsap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideSsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ssap, + AbstractProtocol::FieldValue + ).toBool()); + leSsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ssap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideControl->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ctl, + AbstractProtocol::FieldValue + ).toBool()); + leControl->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ctl, + AbstractProtocol::FieldValue + ).toUInt(), 1)); +} + +void +LlcConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + LlcProtocol::llc_is_override_dsap, + cbOverrideDsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_dsap, + leDsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ssap, + cbOverrideSsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ssap, + leSsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ctl, + cbOverrideControl->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ctl, + leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llcconfig.h b/common/llcconfig.h new file mode 100644 index 0000000..08bd3f4 --- /dev/null +++ b/common/llcconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _LLC_CONFIG_H +#define _LLC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_llc.h" + +class LlcConfigForm : + public AbstractProtocolConfigForm, + private Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); + virtual ~LlcConfigForm(); + + static LlcConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index bbc6758..2a5a373 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -47,15 +47,15 @@ HEADERS = \ HEADERS += \ mac.h \ payload.h \ - dot3.h \ vlan.h \ svlan.h \ vlanstack.h \ eth2.h \ + dot3.h \ + llc.h \ ip6.h HEADERS1 += \ - llc.h \ snap.h \ dot2llc.h \ dot2snap.h \ @@ -91,12 +91,12 @@ SOURCES += \ payload.cpp \ vlan.cpp \ svlan.cpp \ - dot3.cpp \ eth2.cpp \ + dot3.cpp \ + llc.cpp \ ip6.cpp SOURCES1 += \ - llc.cpp \ snap.cpp \ arp.cpp \ ip4.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 4ecf33e..95f1a38 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -11,12 +11,12 @@ FORMS += \ mac.ui \ payload.ui \ vlan.ui \ - dot3.ui \ eth2.ui \ + dot3.ui \ + llc.ui \ ip6.ui \ FORMS1 += \ - llc.ui \ snap.ui \ arp.ui \ ip4.ui \ @@ -52,8 +52,9 @@ HEADERS += \ vlanconfig.h \ svlanconfig.h \ vlanstackconfig.h \ - dot3config.h \ eth2config.h \ + dot3config.h \ + llcconfig.h \ ip6config.h SOURCES += \ @@ -71,8 +72,9 @@ SOURCES += \ macconfig.cpp \ payloadconfig.cpp \ vlanconfig.cpp \ - dot3config.cpp \ eth2config.cpp \ + dot3config.cpp \ + llcconfig.cpp \ ip6config.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 05297e3..ed8a62f 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "llc.h" #include "snap.h" #include "dot2llc.h" #include "dot2snap.h" @@ -44,10 +43,11 @@ along with this program. If not, see #else #include "mac.h" #include "payload.h" -#include "dot3.h" #include "vlan.h" #include "svlan.h" #include "vlanstack.h" +#include "dot3.h" +#include "llc.h" #include "eth2.h" #include "ip6.h" #endif @@ -60,8 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kLlcFieldNumber, - (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, @@ -108,9 +106,6 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadProtocol::createInstance); - registerProtocol(OstProto::Protocol::kDot3FieldNumber, - (void*) Dot3Protocol::createInstance); - registerProtocol(OstProto::Protocol::kVlanFieldNumber, (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kSvlanFieldNumber, @@ -120,6 +115,10 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 1f3e053..591fed6 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -21,11 +21,12 @@ along with this program. If not, see #include "macconfig.h" #include "payloadconfig.h" -#include "dot3config.h" #include "vlanconfig.h" #include "svlanconfig.h" #include "vlanstackconfig.h" #include "eth2config.h" +#include "dot3config.h" +#include "llcconfig.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -44,10 +45,6 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadConfigForm::createInstance); - OstProtocolWidgetFactory->registerProtocolConfigWidget( - OstProto::Protocol::kDot3FieldNumber, - (void*) Dot3ConfigForm::createInstance); - OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kVlanFieldNumber, (void*) VlanConfigForm::createInstance); @@ -61,6 +58,12 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, (void*) Eth2ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kLlcFieldNumber, + (void*) LlcConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, From 4608edf7712bf474beae7fb06976b385d5f208cf Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 27 Mar 2014 06:39:08 +0530 Subject: [PATCH 221/294] NOX: Dot2Llc - Separated protocol and widget as per new framework --- common/dot2llcconfig.h | 36 ++++++++++++++++++++++++++++++++ common/ostproto.pro | 2 +- common/ostprotogui.pro | 1 + common/protocolmanager.cpp | 6 +++--- common/protocolwidgetfactory.cpp | 4 ++++ 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 common/dot2llcconfig.h diff --git a/common/dot2llcconfig.h b/common/dot2llcconfig.h new file mode 100644 index 0000000..76a5b24 --- /dev/null +++ b/common/dot2llcconfig.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _DOT2_LLC_CONFIG_H +#define _DOT2_LLC_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "dot3config.h" +#include "llcconfig.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2LlcFieldNumber, + Dot3ConfigForm, LlcConfigForm, + Dot3Protocol, LlcProtocol + > Dot2LlcConfigForm; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 2a5a373..63524f4 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -53,11 +53,11 @@ HEADERS += \ eth2.h \ dot3.h \ llc.h \ + dot2llc.h \ ip6.h HEADERS1 += \ snap.h \ - dot2llc.h \ dot2snap.h \ arp.h \ ip4.h \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 95f1a38..7a90fe4 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -55,6 +55,7 @@ HEADERS += \ eth2config.h \ dot3config.h \ llcconfig.h \ + dot2llcconfig.h \ ip6config.h SOURCES += \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index ed8a62f..d385d0e 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -23,7 +23,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 #include "snap.h" -#include "dot2llc.h" #include "dot2snap.h" #include "arp.h" #include "ip4.h" @@ -48,6 +47,7 @@ along with this program. If not, see #include "vlanstack.h" #include "dot3.h" #include "llc.h" +#include "dot2llc.h" #include "eth2.h" #include "ip6.h" #endif @@ -62,8 +62,6 @@ ProtocolManager::ProtocolManager() #if 0 registerProtocol(OstProto::Protocol::kSnapFieldNumber, (void*) SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, - (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); @@ -119,6 +117,8 @@ ProtocolManager::ProtocolManager() (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 591fed6..e3bf662 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -27,6 +27,7 @@ along with this program. If not, see #include "eth2config.h" #include "dot3config.h" #include "llcconfig.h" +#include "dot2llcconfig.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -64,6 +65,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kLlcFieldNumber, (void*) LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, From 38c17d5e5bcc87c25dfb5a369bcb782cacf63d64 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 27 Mar 2014 09:56:57 +0530 Subject: [PATCH 222/294] NOX: SNAP - Separated protocl and widget as per new framework --- common/abstractprotocolconfig.h | 14 ++++++ common/ostproto.pro | 4 +- common/ostprotogui.pro | 4 +- common/protocolmanager.cpp | 6 +-- common/protocolwidgetfactory.cpp | 4 ++ common/snap.cpp | 52 --------------------- common/snap.h | 18 ++------ common/snapconfig.cpp | 78 ++++++++++++++++++++++++++++++++ common/snapconfig.h | 41 +++++++++++++++++ 9 files changed, 148 insertions(+), 73 deletions(-) create mode 100644 common/snapconfig.cpp create mode 100644 common/snapconfig.h diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h index e6023ec..ba2f9b7 100644 --- a/common/abstractprotocolconfig.h +++ b/common/abstractprotocolconfig.h @@ -78,6 +78,20 @@ public: { // Do nothing! } + +/*! + Convenience Method - can be used by storeConfigWidget() implementations +*/ + uint hexStrToUInt(QString text, bool *ok=NULL) + { + bool isOk; + uint a_uint = text.remove(QChar(' ')).toUInt(&isOk, 16); + + if (ok) + *ok = isOk; + + return a_uint; + } }; #endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 63524f4..5840244 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -54,10 +54,10 @@ HEADERS += \ dot3.h \ llc.h \ dot2llc.h \ + snap.h \ ip6.h HEADERS1 += \ - snap.h \ dot2snap.h \ arp.h \ ip4.h \ @@ -94,10 +94,10 @@ SOURCES += \ eth2.cpp \ dot3.cpp \ llc.cpp \ + snap.cpp \ ip6.cpp SOURCES1 += \ - snap.cpp \ arp.cpp \ ip4.cpp \ icmp.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 7a90fe4..2d6efc8 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -14,10 +14,10 @@ FORMS += \ eth2.ui \ dot3.ui \ llc.ui \ + snap.ui \ ip6.ui \ FORMS1 += \ - snap.ui \ arp.ui \ ip4.ui \ icmp.ui \ @@ -56,6 +56,7 @@ HEADERS += \ dot3config.h \ llcconfig.h \ dot2llcconfig.h \ + snapconfig.h \ ip6config.h SOURCES += \ @@ -76,6 +77,7 @@ SOURCES += \ eth2config.cpp \ dot3config.cpp \ llcconfig.cpp \ + snapconfig.cpp \ ip6config.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index d385d0e..fb06818 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "snap.h" #include "dot2snap.h" #include "arp.h" #include "ip4.h" @@ -48,6 +47,7 @@ along with this program. If not, see #include "dot3.h" #include "llc.h" #include "dot2llc.h" +#include "snap.h" #include "eth2.h" #include "ip6.h" #endif @@ -60,8 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kSnapFieldNumber, - (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); @@ -119,6 +117,8 @@ ProtocolManager::ProtocolManager() (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index e3bf662..1f153c7 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -28,6 +28,7 @@ along with this program. If not, see #include "dot3config.h" #include "llcconfig.h" #include "dot2llcconfig.h" +#include "snapconfig.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -68,6 +69,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kDot2LlcFieldNumber, (void*) Dot2LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSnapFieldNumber, + (void*) SnapConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, diff --git a/common/snap.cpp b/common/snap.cpp index bdb80c3..6e7e7cc 100644 --- a/common/snap.cpp +++ b/common/snap.cpp @@ -17,28 +17,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "snap.h" quint32 kStdOui = 0x000000; -SnapConfigForm::SnapConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } SnapProtocol::~SnapProtocol() { - delete configForm; } AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, @@ -264,44 +253,3 @@ bool SnapProtocol::setFieldData(int index, const QVariant &value, return isOk; } - -QWidget* SnapProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new SnapConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void SnapProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->cbOverrideOui->setChecked( - fieldData(snap_is_override_oui, FieldValue).toBool()); - configForm->leOui->setText(uintToHexStr( - fieldData(snap_oui, FieldValue).toUInt(), 3)); - - configForm->cbOverrideType->setChecked( - fieldData(snap_is_override_type, FieldValue).toBool()); - configForm->leType->setText(uintToHexStr( - fieldData(snap_type, FieldValue).toUInt(), 2)); -} - -void SnapProtocol::storeConfigWidget() -{ - bool isOk; - configWidget(); - - setFieldData(snap_is_override_oui, - configForm->cbOverrideOui->isChecked()); - setFieldData(snap_oui, configForm->leOui->text().remove(QChar(' ')) - .toUInt(&isOk, BASE_HEX)); - - setFieldData(snap_is_override_type, - configForm->cbOverrideType->isChecked()); - setFieldData(snap_type, configForm->leType->text().remove(QChar(' ')) - .toUInt(&isOk, BASE_HEX)); -} diff --git a/common/snap.h b/common/snap.h index daba802..bf3a349 100644 --- a/common/snap.h +++ b/common/snap.h @@ -23,20 +23,10 @@ along with this program. If not, see #include "abstractprotocol.h" #include "snap.pb.h" -#include "ui_snap.h" - -class SnapConfigForm : public QWidget, public Ui::snap -{ - Q_OBJECT -public: - SnapConfigForm(QWidget *parent = 0); -}; class SnapProtocol : public AbstractProtocol { -private: - OstProto::Snap data; - SnapConfigForm *configForm; +public: enum snapfield { snap_oui = 0, @@ -49,7 +39,6 @@ private: snap_fieldCount }; -public: SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SnapProtocol(); @@ -74,9 +63,8 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Snap data; }; #endif diff --git a/common/snapconfig.cpp b/common/snapconfig.cpp new file mode 100644 index 0000000..e594b57 --- /dev/null +++ b/common/snapconfig.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "snapconfig.h" +#include "snap.h" + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SnapConfigForm::~SnapConfigForm() +{ +} + +SnapConfigForm* SnapConfigForm::createInstance() +{ + return new SnapConfigForm; +} + +void SnapConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideOui->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_oui, + AbstractProtocol::FieldValue + ).toBool()); + leOui->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_oui, + AbstractProtocol::FieldValue + ).toUInt(), 3)); + + cbOverrideType->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void SnapConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SnapProtocol::snap_is_override_oui, + cbOverrideOui->isChecked()); + proto->setFieldData( + SnapProtocol::snap_oui, + hexStrToUInt(leOui->text())); + + proto->setFieldData( + SnapProtocol::snap_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + SnapProtocol::snap_type, + hexStrToUInt(leType->text())); +} diff --git a/common/snapconfig.h b/common/snapconfig.h new file mode 100644 index 0000000..9d230e3 --- /dev/null +++ b/common/snapconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _SNAP_CONFIG_H +#define _SNAP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_snap.h" + +class SnapConfigForm : + public AbstractProtocolConfigForm, + private Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); + virtual ~SnapConfigForm(); + + static SnapConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif From 24fbac9c5a796eff66beab8eb87bb43507558c41 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 27 Mar 2014 18:21:47 +0530 Subject: [PATCH 223/294] NOX: Dot2Snap - Separated protocol and widget as per new framework --- common/dot2snapconfig.h | 36 ++++++++++++++++++++++++++++++++ common/ostproto.pro | 2 +- common/ostprotogui.pro | 1 + common/protocolmanager.cpp | 7 +++---- common/protocolwidgetfactory.cpp | 4 ++++ 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 common/dot2snapconfig.h diff --git a/common/dot2snapconfig.h b/common/dot2snapconfig.h new file mode 100644 index 0000000..32a8d99 --- /dev/null +++ b/common/dot2snapconfig.h @@ -0,0 +1,36 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_CONFIG_H +#define _DOT2_SNAP_CONFIG_H + +#include "comboprotocol.h" + +#include "dot2llcconfig.h" +#include "snapconfig.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2SnapFieldNumber, + Dot2LlcConfigForm, SnapConfigForm, + Dot2LlcProtocol, SnapProtocol + > Dot2SnapConfigForm; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 5840244..0ce73a4 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -55,10 +55,10 @@ HEADERS += \ llc.h \ dot2llc.h \ snap.h \ + dot2snap.h \ ip6.h HEADERS1 += \ - dot2snap.h \ arp.h \ ip4.h \ ipv4addressdelegate.h \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 2d6efc8..1fb6b08 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -57,6 +57,7 @@ HEADERS += \ llcconfig.h \ dot2llcconfig.h \ snapconfig.h \ + dot2snapconfig.h \ ip6config.h SOURCES += \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index fb06818..fa1aaa0 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "dot2snap.h" #include "arp.h" #include "ip4.h" #include "ip6over4.h" @@ -48,6 +47,7 @@ along with this program. If not, see #include "llc.h" #include "dot2llc.h" #include "snap.h" +#include "dot2snap.h" #include "eth2.h" #include "ip6.h" #endif @@ -60,9 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, - (void*) Dot2SnapProtocol::createInstance); - registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, @@ -119,6 +116,8 @@ ProtocolManager::ProtocolManager() (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 1f153c7..2293646 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -29,6 +29,7 @@ along with this program. If not, see #include "llcconfig.h" #include "dot2llcconfig.h" #include "snapconfig.h" +#include "dot2snapconfig.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -72,6 +73,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSnapFieldNumber, (void*) SnapConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, From e6339e3d0962f0c5106817b96371ee6ca38f73b3 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 28 Mar 2014 05:52:15 +0530 Subject: [PATCH 224/294] NOX: Arp - separated protocol and widget as per new framework --- common/abstractprotocolconfig.h | 14 ++ common/arp.cpp | 175 +------------------- common/arp.h | 26 +-- common/arpconfig.cpp | 270 +++++++++++++++++++++++++++++++ common/arpconfig.h | 46 ++++++ common/ostproto.pro | 4 +- common/ostprotogui.pro | 4 +- common/protocolmanager.cpp | 6 +- common/protocolwidgetfactory.cpp | 4 + 9 files changed, 347 insertions(+), 202 deletions(-) create mode 100644 common/arpconfig.cpp create mode 100644 common/arpconfig.h diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h index ba2f9b7..4172a7c 100644 --- a/common/abstractprotocolconfig.h +++ b/common/abstractprotocolconfig.h @@ -87,6 +87,20 @@ public: bool isOk; uint a_uint = text.remove(QChar(' ')).toUInt(&isOk, 16); + if (ok) + *ok = isOk; + + return a_uint; + } + +/*! + Convenience Method - can be used by storeConfigWidget() implementations +*/ + quint64 hexStrToUInt64(QString text, bool *ok=NULL) + { + bool isOk; + quint64 a_uint = text.remove(QChar(' ')).toULongLong(&isOk, 16); + if (ok) *ok = isOk; diff --git a/common/arp.cpp b/common/arp.cpp index 84b10a8..11675a1 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -17,84 +17,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "arp.h" -ArpConfigForm::ArpConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); - opCodeCombo->addItem(1, "ARP Request"); - opCodeCombo->addItem(2, "ARP Reply"); - - connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), - SLOT(on_senderHwAddrMode_currentIndexChanged(int))); - connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), - SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); - connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), - SLOT(on_targetHwAddrMode_currentIndexChanged(int))); - connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), - SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); -} - -void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Arp::kFixed) - senderHwAddrCount->setDisabled(true); - else - senderHwAddrCount->setEnabled(true); -} - -void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Arp::kFixed) - targetHwAddrCount->setDisabled(true); - else - targetHwAddrCount->setEnabled(true); -} - -void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Arp::kFixedHost) - { - senderProtoAddrCount->setDisabled(true); - senderProtoAddrMask->setDisabled(true); - } - else - { - senderProtoAddrCount->setEnabled(true); - senderProtoAddrMask->setEnabled(true); - } -} - -void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Arp::kFixedHost) - { - targetProtoAddrCount->setDisabled(true); - targetProtoAddrMask->setDisabled(true); - } - else - { - targetProtoAddrCount->setEnabled(true); - targetProtoAddrMask->setEnabled(true); - } -} +#include ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { _hasPayload = false; - configForm = NULL; } ArpProtocol::~ArpProtocol() { - delete configForm; } AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, @@ -884,110 +818,3 @@ int ArpProtocol::protocolFrameVariableCount() const return count; } - -QWidget* ArpProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new ArpConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void ArpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->hwType->setText( - fieldData(arp_hwType, FieldValue).toString()); - configForm->protoType->setText(uintToHexStr( - fieldData(arp_protoType, FieldValue).toUInt(), 2)); - configForm->hwAddrLen->setText( - fieldData(arp_hwAddrLen, FieldValue).toString()); - configForm->protoAddrLen->setText( - fieldData(arp_protoAddrLen, FieldValue).toString()); - - configForm->opCodeCombo->setValue( - fieldData(arp_opCode, FieldValue).toUInt()); - - configForm->senderHwAddr->setText(uintToHexStr( - fieldData(arp_senderHwAddr, FieldValue).toULongLong(), 6)); - configForm->senderHwAddrMode->setCurrentIndex( - fieldData(arp_senderHwAddrMode, FieldValue).toUInt()); - configForm->senderHwAddrCount->setText( - fieldData(arp_senderHwAddrCount, FieldValue).toString()); - - configForm->senderProtoAddr->setText(QHostAddress( - fieldData(arp_senderProtoAddr, FieldValue).toUInt()).toString()); - configForm->senderProtoAddrMode->setCurrentIndex( - fieldData(arp_senderProtoAddrMode, FieldValue).toUInt()); - configForm->senderProtoAddrCount->setText( - fieldData(arp_senderProtoAddrCount, FieldValue).toString()); - configForm->senderProtoAddrMask->setText(QHostAddress( - fieldData(arp_senderProtoAddrMask, FieldValue).toUInt()).toString()); - - configForm->targetHwAddr->setText(uintToHexStr( - fieldData(arp_targetHwAddr, FieldValue).toULongLong(), 6)); - configForm->targetHwAddrMode->setCurrentIndex( - fieldData(arp_targetHwAddrMode, FieldValue).toUInt()); - configForm->targetHwAddrCount->setText( - fieldData(arp_targetHwAddrCount, FieldValue).toString()); - - configForm->targetProtoAddr->setText(QHostAddress( - fieldData(arp_targetProtoAddr, FieldValue).toUInt()).toString()); - configForm->targetProtoAddrMode->setCurrentIndex( - fieldData(arp_targetProtoAddrMode, FieldValue).toUInt()); - configForm->targetProtoAddrCount->setText( - fieldData(arp_targetProtoAddrCount, FieldValue).toString()); - configForm->targetProtoAddrMask->setText(QHostAddress( - fieldData(arp_targetProtoAddrMask, FieldValue).toUInt()).toString()); - -} - -void ArpProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(arp_hwType, configForm->hwType->text()); - setFieldData(arp_protoType, configForm->protoType->text().toUInt( - &isOk, BASE_HEX)); - setFieldData(arp_hwAddrLen, configForm->hwAddrLen->text()); - setFieldData(arp_protoAddrLen, configForm->protoAddrLen->text()); - - setFieldData(arp_opCode, configForm->opCodeCombo->currentValue()); - - setFieldData(arp_senderHwAddr, configForm->senderHwAddr->text() - .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); - setFieldData(arp_senderHwAddrMode, - configForm->senderHwAddrMode->currentIndex()); - setFieldData(arp_senderHwAddrCount, configForm->senderHwAddrCount->text()); - - setFieldData(arp_senderProtoAddr, QHostAddress( - configForm->senderProtoAddr->text()).toIPv4Address()); - setFieldData(arp_senderProtoAddrMode, - configForm->senderProtoAddrMode->currentIndex()); - setFieldData(arp_senderProtoAddrCount, - configForm->senderProtoAddrCount->text()); - setFieldData(arp_senderProtoAddrMask, QHostAddress( - configForm->senderProtoAddrMask->text()).toIPv4Address()); - - setFieldData(arp_targetHwAddr, configForm->targetHwAddr->text() - .remove(QChar(' ')).toULongLong(&isOk, BASE_HEX)); - setFieldData(arp_targetHwAddrMode, - configForm->targetHwAddrMode->currentIndex()); - setFieldData(arp_targetHwAddrCount, configForm->targetHwAddrCount->text()); - - setFieldData(arp_targetProtoAddr, QHostAddress( - configForm->targetProtoAddr->text()).toIPv4Address()); - setFieldData(arp_targetProtoAddrMode, - configForm->targetProtoAddrMode->currentIndex()); - setFieldData(arp_targetProtoAddrCount, - configForm->targetProtoAddrCount->text()); - setFieldData(arp_targetProtoAddrMask, QHostAddress( - configForm->targetProtoAddrMask->text()).toIPv4Address()); -} - diff --git a/common/arp.h b/common/arp.h index d5582d2..6b674f9 100644 --- a/common/arp.h +++ b/common/arp.h @@ -20,10 +20,8 @@ along with this program. If not, see #ifndef _ARP_H #define _ARP_H -#include "arp.pb.h" -#include "ui_arp.h" - #include "abstractprotocol.h" +#include "arp.pb.h" /* Arp Protocol Frame Format - @@ -34,23 +32,9 @@ Arp Protocol Frame Format - Figures in brackets represent field width in bytes */ -class ArpConfigForm : public QWidget, public Ui::Arp -{ - Q_OBJECT -public: - ArpConfigForm(QWidget *parent = 0); -private slots: - void on_senderHwAddrMode_currentIndexChanged(int index); - void on_senderProtoAddrMode_currentIndexChanged(int index); - void on_targetHwAddrMode_currentIndexChanged(int index); - void on_targetProtoAddrMode_currentIndexChanged(int index); -}; - class ArpProtocol : public AbstractProtocol { -private: - OstProto::Arp data; - ArpConfigForm *configForm; +public: enum arpfield { // Frame Fields @@ -86,7 +70,6 @@ private: arp_fieldCount }; -public: ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~ArpProtocol(); @@ -113,9 +96,8 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Arp data; }; #endif diff --git a/common/arpconfig.cpp b/common/arpconfig.cpp new file mode 100644 index 0000000..e24dfe4 --- /dev/null +++ b/common/arpconfig.cpp @@ -0,0 +1,270 @@ +/* +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 +*/ + +#include "arpconfig.h" +#include "arp.h" + +#include + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +ArpConfigForm::~ArpConfigForm() +{ +} + +ArpConfigForm* ArpConfigForm::createInstance() +{ + return new ArpConfigForm; +} + +void ArpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hwType->setText( + proto->fieldData( + ArpProtocol::arp_hwType, + AbstractProtocol::FieldValue + ).toString()); + protoType->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_protoType, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + hwAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_hwAddrLen, + AbstractProtocol::FieldValue + ).toString()); + protoAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_protoAddrLen, + AbstractProtocol::FieldValue + ).toString()); + + opCodeCombo->setValue( + proto->fieldData( + ArpProtocol::arp_opCode, + AbstractProtocol::FieldValue + ).toUInt()); + + senderHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_senderHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + senderHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + senderProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + senderProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + senderProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + targetHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_targetHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + targetHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + targetProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + targetProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + targetProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void ArpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + ArpProtocol::arp_hwType, + hwType->text()); + proto->setFieldData( + ArpProtocol::arp_protoType, + hexStrToUInt(protoType->text())); + proto->setFieldData( + ArpProtocol::arp_hwAddrLen, + hwAddrLen->text()); + proto->setFieldData( + ArpProtocol::arp_protoAddrLen, + protoAddrLen->text()); + + proto->setFieldData( + ArpProtocol::arp_opCode, + opCodeCombo->currentValue()); + + proto->setFieldData( + ArpProtocol::arp_senderHwAddr, + hexStrToUInt64(senderHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrMode, + senderHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrCount, + senderHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_senderProtoAddr, + QHostAddress(senderProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMode, + senderProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrCount, + senderProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMask, + QHostAddress(senderProtoAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + ArpProtocol::arp_targetHwAddr, + hexStrToUInt64(targetHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrMode, + targetHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrCount, + targetHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_targetProtoAddr, + QHostAddress(targetProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMode, + targetProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrCount, + targetProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMask, + QHostAddress(targetProtoAddrMask->text()).toIPv4Address()); +} + +/* + * ------------ Private Slots -------------- + */ + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + diff --git a/common/arpconfig.h b/common/arpconfig.h new file mode 100644 index 0000000..15aa4bc --- /dev/null +++ b/common/arpconfig.h @@ -0,0 +1,46 @@ +/* +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 +*/ + +#ifndef _ARP_CONFIG_H +#define _ARP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_arp.h" + +class ArpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); + virtual ~ArpConfigForm(); + + static ArpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 0ce73a4..1360503 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -56,10 +56,10 @@ HEADERS += \ dot2llc.h \ snap.h \ dot2snap.h \ + arp.h \ ip6.h HEADERS1 += \ - arp.h \ ip4.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ @@ -95,10 +95,10 @@ SOURCES += \ dot3.cpp \ llc.cpp \ snap.cpp \ + arp.cpp \ ip6.cpp SOURCES1 += \ - arp.cpp \ ip4.cpp \ icmp.cpp \ gmp.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 1fb6b08..43e88f8 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -15,10 +15,10 @@ FORMS += \ dot3.ui \ llc.ui \ snap.ui \ + arp.ui \ ip6.ui \ FORMS1 += \ - arp.ui \ ip4.ui \ icmp.ui \ gmp.ui \ @@ -58,6 +58,7 @@ HEADERS += \ dot2llcconfig.h \ snapconfig.h \ dot2snapconfig.h \ + arpconfig.h \ ip6config.h SOURCES += \ @@ -79,6 +80,7 @@ SOURCES += \ dot3config.cpp \ llcconfig.cpp \ snapconfig.cpp \ + arpconfig.cpp \ ip6config.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index fa1aaa0..f7982f5 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "arp.h" #include "ip4.h" #include "ip6over4.h" #include "ip4over6.h" @@ -49,6 +48,7 @@ along with this program. If not, see #include "snap.h" #include "dot2snap.h" #include "eth2.h" +#include "arp.h" #include "ip6.h" #endif @@ -60,8 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kArpFieldNumber, - (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, @@ -119,6 +117,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); #endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 2293646..eea4cc5 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -30,6 +30,7 @@ along with this program. If not, see #include "dot2llcconfig.h" #include "snapconfig.h" #include "dot2snapconfig.h" +#include "arpconfig.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -77,6 +78,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kArpFieldNumber, + (void*) ArpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, (void*) Ip6ConfigForm::createInstance); From d818b90a8f2a4c81fa4c49667aec9f3294aa1e29 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 3 Apr 2014 07:23:04 +0530 Subject: [PATCH 225/294] NOX: IPv4 - Separated protocol and widget as per new framework --- common/ip4.cpp | 388 +++++++++++++++++++------------ common/ip4.h | 25 +- common/ip4config.cpp | 297 +++++++++++++++++++++++ common/ip4config.h | 44 ++++ common/ostproto.pro | 4 +- common/ostprotogui.pro | 6 +- common/protocolmanager.cpp | 6 +- common/protocolwidgetfactory.cpp | 4 + 8 files changed, 599 insertions(+), 175 deletions(-) create mode 100644 common/ip4config.cpp create mode 100644 common/ip4config.h diff --git a/common/ip4.cpp b/common/ip4.cpp index a7d6ef7..1c1557b 100644 --- a/common/ip4.cpp +++ b/common/ip4.cpp @@ -17,66 +17,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "ip4.h" -Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - leIpVersion->setValidator(new QIntValidator(0, 15, this)); - - connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); - connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); -} - -Ip4ConfigForm::~Ip4ConfigForm() -{ - qDebug("IPv4 Config Form destructor called"); -} - -void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Ip4::e_im_fixed) - { - leIpSrcAddrCount->setDisabled(true); - leIpSrcAddrMask->setDisabled(true); - } - else - { - leIpSrcAddrCount->setEnabled(true); - leIpSrcAddrMask->setEnabled(true); - } -} - -void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) -{ - if (index == OstProto::Ip4::e_im_fixed) - { - leIpDstAddrCount->setDisabled(true); - leIpDstAddrMask->setDisabled(true); - } - else - { - leIpDstAddrCount->setEnabled(true); - leIpDstAddrMask->setEnabled(true); - } -} +#include Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } Ip4Protocol::~Ip4Protocol() { - delete configForm; } AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, @@ -558,21 +509,87 @@ QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, } break; } - // Meta fields + // Meta fields case ip4_isOverrideVer: + switch(attrib) + { + case FieldValue: return data.is_override_ver(); + default: break; + } + break; case ip4_isOverrideHdrLen: + switch(attrib) + { + case FieldValue: return data.is_override_hdrlen(); + default: break; + } + break; case ip4_isOverrideTotLen: + switch(attrib) + { + case FieldValue: return data.is_override_totlen(); + default: break; + } + break; case ip4_isOverrideProto: + switch(attrib) + { + case FieldValue: return data.is_override_proto(); + default: break; + } + break; case ip4_isOverrideCksum: + switch(attrib) + { + case FieldValue: return data.is_override_cksum(); + default: break; + } + break; case ip4_srcAddrMode: + switch(attrib) + { + case FieldValue: return data.src_ip_mode(); + default: break; + } + break; case ip4_srcAddrCount: + switch(attrib) + { + case FieldValue: return data.src_ip_count(); + default: break; + } + break; case ip4_srcAddrMask: + switch(attrib) + { + case FieldValue: return data.src_ip_mask(); + default: break; + } + break; case ip4_dstAddrMode: + switch(attrib) + { + case FieldValue: return data.dst_ip_mode(); + default: break; + } + break; case ip4_dstAddrCount: + switch(attrib) + { + case FieldValue: return data.dst_ip_count(); + default: break; + } + break; case ip4_dstAddrMask: + switch(attrib) + { + case FieldValue: return data.dst_ip_mask(); + default: break; + } + break; default: break; } @@ -586,19 +603,191 @@ bool Ip4Protocol::setFieldData(int index, const QVariant &value, bool isOk = false; if (attrib != FieldValue) - return false; + goto _exit; switch (index) { + case ip4_ver: + { + uint version = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + ((version & 0xF) << 4) + | (data.ver_hdrlen() & 0x0F)); + break; + } + case ip4_hdrLen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + (data.ver_hdrlen() & 0xF0) + | (hdrLen & 0x0F)); + break; + } + case ip4_tos: + { + uint tos = value.toUInt(&isOk); + if (isOk) + data.set_tos(tos); + break; + } + case ip4_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case ip4_id: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_id(id); + break; + } + case ip4_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case ip4_fragOfs: + { + uint fragOfs = value.toUInt(&isOk); + if (isOk) + data.set_frag_ofs(fragOfs); + break; + } + case ip4_ttl: + { + uint ttl = value.toUInt(&isOk); + if (isOk) + data.set_ttl(ttl); + break; + } case ip4_proto: { uint proto = value.toUInt(&isOk); if (isOk) data.set_proto(proto); + break; } + case ip4_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case ip4_srcAddr: + { + quint32 srcIp = value.toUInt(&isOk); + if (isOk) + data.set_src_ip(srcIp); + break; + } + case ip4_dstAddr: + { + quint32 dstIp = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip(dstIp); + break; + } + + // Meta-fields + case ip4_isOverrideVer: + { + bool ovr = value.toBool(); + data.set_is_override_ver(ovr); + isOk = true; + break; + } + case ip4_isOverrideHdrLen: + { + bool ovr = value.toBool(); + data.set_is_override_hdrlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideTotLen: + { + bool ovr = value.toBool(); + data.set_is_override_totlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideProto: + { + bool ovr = value.toBool(); + data.set_is_override_proto(ovr); + isOk = true; + break; + } + case ip4_isOverrideCksum: + { + bool ovr = value.toBool(); + data.set_is_override_cksum(ovr); + isOk = true; + break; + } + + case ip4_srcAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_src_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_srcAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_count(count); + break; + } + case ip4_srcAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_mask(mask); + break; + } + + case ip4_dstAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_dst_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_dstAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_count(count); + break; + } + case ip4_dstAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_mask(mask); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); break; } + +_exit: return isOk; } @@ -655,98 +844,3 @@ quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } - -QWidget* Ip4Protocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new Ip4ConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void Ip4Protocol::loadConfigWidget() -{ - configWidget(); - - configForm->cbIpVersionOverride->setChecked(data.is_override_ver()); - configForm->leIpVersion->setText(fieldData(ip4_ver, FieldValue).toString()); - - configForm->cbIpHdrLenOverride->setChecked(data.is_override_hdrlen()); - configForm->leIpHdrLen->setText(fieldData(ip4_hdrLen, FieldValue).toString()); - - configForm->leIpTos->setText(uintToHexStr(data.tos(), 1)); - - configForm->cbIpLengthOverride->setChecked(data.is_override_totlen()); - configForm->leIpLength->setText(fieldData(ip4_totLen, FieldValue).toString()); - - configForm->leIpId->setText(uintToHexStr(data.id(), 2)); - configForm->leIpFragOfs->setText(QString().setNum(data.frag_ofs())); - configForm->cbIpFlagsDf->setChecked((data.flags() & IP_FLAG_DF) > 0); - configForm->cbIpFlagsMf->setChecked((data.flags() & IP_FLAG_MF) > 0); - - configForm->leIpTtl->setText(QString().setNum(data.ttl())); - - configForm->cbIpProtocolOverride->setChecked(data.is_override_proto()); - configForm->leIpProto->setText(uintToHexStr( - fieldData(ip4_proto, FieldValue).toUInt(), 1)); - - configForm->cbIpCksumOverride->setChecked(data.is_override_cksum()); - configForm->leIpCksum->setText(uintToHexStr( - fieldData(ip4_cksum, FieldValue).toUInt(), 2)); - - configForm->leIpSrcAddr->setText(QHostAddress(data.src_ip()).toString()); - configForm->cmbIpSrcAddrMode->setCurrentIndex(data.src_ip_mode()); - configForm->leIpSrcAddrCount->setText(QString().setNum(data.src_ip_count())); - configForm->leIpSrcAddrMask->setText(QHostAddress(data.src_ip_mask()).toString()); - - configForm->leIpDstAddr->setText(QHostAddress(data.dst_ip()).toString()); - configForm->cmbIpDstAddrMode->setCurrentIndex(data.dst_ip_mode()); - configForm->leIpDstAddrCount->setText(QString().setNum(data.dst_ip_count())); - configForm->leIpDstAddrMask->setText(QHostAddress(data.dst_ip_mask()).toString()); -} - -void Ip4Protocol::storeConfigWidget() -{ - uint ff = 0; - bool isOk; - - configWidget(); - - data.set_is_override_ver(configForm->cbIpVersionOverride->isChecked()); - data.set_ver_hdrlen(((configForm->leIpVersion->text().toULong(&isOk) & 0x0F) << 4) | - (configForm->leIpHdrLen->text().toULong(&isOk) & 0x0F)); - data.set_is_override_hdrlen(configForm->cbIpHdrLenOverride->isChecked()); - - data.set_tos(configForm->leIpTos->text().toULong(&isOk, 16)); - - data.set_totlen(configForm->leIpLength->text().toULong(&isOk)); - data.set_is_override_totlen(configForm->cbIpLengthOverride->isChecked()); - - data.set_id(configForm->leIpId->text().remove(QChar(' ')).toULong(&isOk, 16)); - data.set_frag_ofs(configForm->leIpFragOfs->text().toULong(&isOk)); - - if (configForm->cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; - if (configForm->cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; - data.set_flags(ff); - - data.set_ttl(configForm->leIpTtl->text().toULong(&isOk)); - - data.set_is_override_proto(configForm->cbIpProtocolOverride->isChecked()); - data.set_proto(configForm->leIpProto->text().remove(QChar(' ')).toULong(&isOk, 16)); - - data.set_is_override_cksum(configForm->cbIpCksumOverride->isChecked()); - data.set_cksum(configForm->leIpCksum->text().remove(QChar(' ')).toULong(&isOk, 16)); - - data.set_src_ip(QHostAddress(configForm->leIpSrcAddr->text()).toIPv4Address()); - data.set_src_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpSrcAddrMode->currentIndex()); - data.set_src_ip_count(configForm->leIpSrcAddrCount->text().toULong(&isOk)); - data.set_src_ip_mask(QHostAddress(configForm->leIpSrcAddrMask->text()).toIPv4Address()); - - data.set_dst_ip(QHostAddress(configForm->leIpDstAddr->text()).toIPv4Address()); - data.set_dst_ip_mode((OstProto::Ip4_IpAddrMode)configForm->cmbIpDstAddrMode->currentIndex()); - data.set_dst_ip_count(configForm->leIpDstAddrCount->text().toULong(&isOk)); - data.set_dst_ip_mask(QHostAddress(configForm->leIpDstAddrMask->text()).toIPv4Address()); -} - diff --git a/common/ip4.h b/common/ip4.h index 6f89b09..1ea8128 100644 --- a/common/ip4.h +++ b/common/ip4.h @@ -21,31 +21,15 @@ along with this program. If not, see #define _IPV4_H #include "abstractprotocol.h" - #include "ip4.pb.h" -#include "ui_ip4.h" #define IP_FLAG_MF 0x1 #define IP_FLAG_DF 0x2 #define IP_FLAG_UNUSED 0x4 - -class Ip4ConfigForm : public QWidget, public Ui::ip4 -{ - Q_OBJECT -public: - Ip4ConfigForm(QWidget *parent = 0); - ~Ip4ConfigForm(); -private slots: - void on_cmbIpSrcAddrMode_currentIndexChanged(int index); - void on_cmbIpDstAddrMode_currentIndexChanged(int index); -}; - class Ip4Protocol : public AbstractProtocol { -private: - OstProto::Ip4 data; - Ip4ConfigForm *configForm; +public: enum ip4field { ip4_ver = 0, @@ -61,6 +45,7 @@ private: ip4_srcAddr, ip4_dstAddr, + // Meta-fields ip4_isOverrideVer, ip4_isOverrideHdrLen, ip4_isOverrideTotLen, @@ -78,7 +63,6 @@ private: ip4_fieldCount }; -public: Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Ip4Protocol(); @@ -107,9 +91,8 @@ public: virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Ip4 data; }; diff --git a/common/ip4config.cpp b/common/ip4config.cpp new file mode 100644 index 0000000..261da7a --- /dev/null +++ b/common/ip4config.cpp @@ -0,0 +1,297 @@ +/* +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 +*/ + +#include "ip4config.h" +#include "ip4.h" + +#include + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ +} + + +Ip4ConfigForm* Ip4ConfigForm::createInstance() +{ + return new Ip4ConfigForm; +} + +void Ip4ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbIpVersionOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideVer, + AbstractProtocol::FieldValue + ).toBool()); + leIpVersion->setText( + proto->fieldData( + Ip4Protocol::ip4_ver, + AbstractProtocol::FieldValue + ).toString()); + + cbIpHdrLenOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpHdrLen->setText( + proto->fieldData( + Ip4Protocol::ip4_hdrLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpTos->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_tos, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpLengthOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpLength->setText( + proto->fieldData( + Ip4Protocol::ip4_totLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpId->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_id, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + leIpFragOfs->setText( + proto->fieldData( + Ip4Protocol::ip4_fragOfs, + AbstractProtocol::FieldValue + ).toString()); + cbIpFlagsDf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_DF) > 0); + cbIpFlagsMf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_MF) > 0); + + leIpTtl->setText( + proto->fieldData( + Ip4Protocol::ip4_ttl, + AbstractProtocol::FieldValue + ).toString()); + + cbIpProtocolOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideProto, + AbstractProtocol::FieldValue + ).toBool()); + leIpProto->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_proto, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpCksumOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); + leIpCksum->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + leIpSrcAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpSrcAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpSrcAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpSrcAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + leIpDstAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpDstAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpDstAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpDstAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void Ip4ConfigForm::storeWidget(AbstractProtocol *proto) +{ + uint ff = 0; + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideVer, + cbIpVersionOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_ver, + leIpVersion->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + cbIpHdrLenOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_hdrLen, + leIpHdrLen->text()); + + proto->setFieldData( + Ip4Protocol::ip4_tos, + hexStrToUInt(leIpTos->text())); + + proto->setFieldData( + Ip4Protocol::ip4_totLen, + leIpLength->text()); + proto->setFieldData( + Ip4Protocol::ip4_isOverrideTotLen, + cbIpLengthOverride->isChecked()); + + proto->setFieldData( + Ip4Protocol::ip4_id, + hexStrToUInt(leIpId->text())); + proto->setFieldData( + Ip4Protocol::ip4_fragOfs, + leIpFragOfs->text()); + + if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + proto->setFieldData( + Ip4Protocol::ip4_flags, + ff); + + proto->setFieldData( + Ip4Protocol::ip4_ttl, + leIpTtl->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideProto, + cbIpProtocolOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_proto, + hexStrToUInt(leIpProto->text())); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideCksum, + cbIpCksumOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_cksum, + hexStrToUInt(leIpCksum->text())); + + proto->setFieldData( + Ip4Protocol::ip4_srcAddr, + QHostAddress(leIpSrcAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpSrcAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrCount, + leIpSrcAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMask, + QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + Ip4Protocol::ip4_dstAddr, + QHostAddress(leIpDstAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpDstAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrCount, + leIpDstAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMask, + QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); +} + +/* + * Slots + */ +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} diff --git a/common/ip4config.h b/common/ip4config.h new file mode 100644 index 0000000..6db7b97 --- /dev/null +++ b/common/ip4config.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _IPV4_CONFIG_H +#define _IPV4_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip4.h" + +class Ip4ConfigForm : + public AbstractProtocolConfigForm, + private Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + virtual ~Ip4ConfigForm(); + + static Ip4ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 1360503..fcf36ab 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -57,10 +57,10 @@ HEADERS += \ snap.h \ dot2snap.h \ arp.h \ + ip4.h \ ip6.h HEADERS1 += \ - ip4.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ ip6over4.h \ @@ -96,10 +96,10 @@ SOURCES += \ llc.cpp \ snap.cpp \ arp.cpp \ + ip4.cpp \ ip6.cpp SOURCES1 += \ - ip4.cpp \ icmp.cpp \ gmp.cpp \ igmp.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 43e88f8..401d355 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -16,10 +16,10 @@ FORMS += \ llc.ui \ snap.ui \ arp.ui \ - ip6.ui \ + ip4.ui \ + ip6.ui FORMS1 += \ - ip4.ui \ icmp.ui \ gmp.ui \ tcp.ui \ @@ -59,6 +59,7 @@ HEADERS += \ snapconfig.h \ dot2snapconfig.h \ arpconfig.h \ + ip4config.h \ ip6config.h SOURCES += \ @@ -81,6 +82,7 @@ SOURCES += \ llcconfig.cpp \ snapconfig.cpp \ arpconfig.cpp \ + ip4config.cpp \ ip6config.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index f7982f5..b0db944 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "ip4.h" #include "ip6over4.h" #include "ip4over6.h" #include "ip4over4.h" @@ -49,6 +48,7 @@ along with this program. If not, see #include "dot2snap.h" #include "eth2.h" #include "arp.h" +#include "ip4.h" #include "ip6.h" #endif @@ -60,8 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kIp4FieldNumber, - (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, (void*) Ip6over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, @@ -119,6 +117,8 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); #endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index eea4cc5..bfbf749 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -31,6 +31,7 @@ along with this program. If not, see #include "snapconfig.h" #include "dot2snapconfig.h" #include "arpconfig.h" +#include "ip4config.h" #include "ip6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -81,6 +82,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kArpFieldNumber, (void*) ArpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, (void*) Ip6ConfigForm::createInstance); From 710b295d7441de42ea91b5dfbca78162050e928c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 5 Apr 2014 05:43:26 +0530 Subject: [PATCH 226/294] NOX: IPv4over4 - Separated protocol and widget as per new framework --- common/ip4over4config.h | 35 ++++++++++++++++++++++++++++++++ common/ostproto.pro | 4 ++-- common/ostprotogui.pro | 3 ++- common/protocolmanager.cpp | 7 ++++--- common/protocolwidgetfactory.cpp | 5 +++++ 5 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 common/ip4over4config.h diff --git a/common/ip4over4config.h b/common/ip4over4config.h new file mode 100644 index 0000000..328b14e --- /dev/null +++ b/common/ip4over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_CONFIG_H +#define _IP_4_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip4.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over4FieldNumber, + Ip4ConfigForm, Ip4ConfigForm, + Ip4Protocol, Ip4Protocol + > Ip4over4ConfigForm; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index fcf36ab..42bfb59 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -58,14 +58,14 @@ HEADERS += \ dot2snap.h \ arp.h \ ip4.h \ - ip6.h + ip6.h \ + ip4over4.h HEADERS1 += \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ ip6over4.h \ ip4over6.h \ - ip4over4.h \ ip6over6.h \ icmp.h \ gmp.h \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 401d355..80fbab7 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -60,7 +60,8 @@ HEADERS += \ dot2snapconfig.h \ arpconfig.h \ ip4config.h \ - ip6config.h + ip6config.h \ + ip4over4config.h SOURCES += \ ostprotolib.cpp \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index b0db944..35e7340 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -24,7 +24,6 @@ along with this program. If not, see #if 0 #include "ip6over4.h" #include "ip4over6.h" -#include "ip4over4.h" #include "ip6over6.h" #include "icmp.h" #include "igmp.h" @@ -50,6 +49,7 @@ along with this program. If not, see #include "arp.h" #include "ip4.h" #include "ip6.h" +#include "ip4over4.h" #endif ProtocolManager *OstProtocolManager; @@ -64,8 +64,6 @@ ProtocolManager::ProtocolManager() (void*) Ip6over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, (void*) Ip4over6Protocol::createInstance); - registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, - (void*) Ip4over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, (void*) Ip6over6Protocol::createInstance); @@ -121,6 +119,9 @@ ProtocolManager::ProtocolManager() (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index bfbf749..3962a90 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -33,6 +33,7 @@ along with this program. If not, see #include "arpconfig.h" #include "ip4config.h" #include "ip6config.h" +#include "ip4over4config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -88,6 +89,10 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, (void*) Ip6ConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4ConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() From 3ba67e9ee36dc1cc2eb4aad111c6862a1c3cd322 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 5 Apr 2014 14:59:12 +0530 Subject: [PATCH 227/294] NOX: IPv4over6, IPv6over4, IPv6over6 - separated protocol and widget as per new framework --- common/ip4over6config.h | 35 ++++++++++++++++++++++++++++++++ common/ip6over4config.h | 35 ++++++++++++++++++++++++++++++++ common/ip6over6config.h | 33 ++++++++++++++++++++++++++++++ common/ostproto.pro | 8 ++++---- common/protocolmanager.cpp | 19 ++++++++--------- common/protocolwidgetfactory.cpp | 12 +++++++++++ 6 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 common/ip4over6config.h create mode 100644 common/ip6over4config.h create mode 100644 common/ip6over6config.h diff --git a/common/ip4over6config.h b/common/ip4over6config.h new file mode 100644 index 0000000..b0ccf63 --- /dev/null +++ b/common/ip4over6config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_CONFIG_H +#define _IP_4_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over6FieldNumber, + Ip6ConfigForm, Ip4ConfigForm, + Ip6Protocol, Ip4Protocol + > Ip4over6ConfigForm; + +#endif diff --git a/common/ip6over4config.h b/common/ip6over4config.h new file mode 100644 index 0000000..b3c2390 --- /dev/null +++ b/common/ip6over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_CONFIG_H +#define _IP_6_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over4FieldNumber, + Ip4ConfigForm, Ip6ConfigForm, + Ip4Protocol, Ip6Protocol + > Ip6over4ConfigForm; + +#endif diff --git a/common/ip6over6config.h b/common/ip6over6config.h new file mode 100644 index 0000000..4324fe7 --- /dev/null +++ b/common/ip6over6config.h @@ -0,0 +1,33 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_CONFIG_H +#define _IP_6_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip6config.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over6FieldNumber, + Ip6ConfigForm, Ip6ConfigForm, + Ip6Protocol, Ip6Protocol + > Ip6over6ConfigForm; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 42bfb59..2d7265f 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -59,14 +59,14 @@ HEADERS += \ arp.h \ ip4.h \ ip6.h \ - ip4over4.h + ip4over4.h \ + ip4over6.h \ + ip6over4.h \ + ip6over6.h HEADERS1 += \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ - ip6over4.h \ - ip4over6.h \ - ip6over6.h \ icmp.h \ gmp.h \ igmp.h \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 35e7340..175864e 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,9 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "ip6over4.h" -#include "ip4over6.h" -#include "ip6over6.h" #include "icmp.h" #include "igmp.h" #include "mld.h" @@ -50,6 +47,9 @@ along with this program. If not, see #include "ip4.h" #include "ip6.h" #include "ip4over4.h" +#include "ip4over6.h" +#include "ip6over4.h" +#include "ip6over6.h" #endif ProtocolManager *OstProtocolManager; @@ -60,13 +60,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, - (void*) Ip6over4Protocol::createInstance); - registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, - (void*) Ip4over6Protocol::createInstance); - registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, - (void*) Ip6over6Protocol::createInstance); - registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIgmpFieldNumber, @@ -122,6 +115,12 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 3962a90..1d7f5bb 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -34,6 +34,9 @@ along with this program. If not, see #include "ip4config.h" #include "ip6config.h" #include "ip4over4config.h" +#include "ip4over6config.h" +#include "ip6over4config.h" +#include "ip6over6config.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -93,6 +96,15 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6ConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() From 21b606e9ea81d5e6d475b7d027083c851422f021 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 8 Apr 2014 05:52:27 +0530 Subject: [PATCH 228/294] NOX: TCP - Separated protocol and widget as per new framework --- common/ostproto.pro | 8 +- common/ostprotogui.pro | 10 +- common/protocolmanager.cpp | 11 +- common/protocolwidgetfactory.cpp | 7 ++ common/tcp.cpp | 103 ------------------ common/tcp.h | 18 +--- common/tcpconfig.cpp | 175 +++++++++++++++++++++++++++++++ common/tcpconfig.h | 41 ++++++++ 8 files changed, 244 insertions(+), 129 deletions(-) create mode 100644 common/tcpconfig.cpp create mode 100644 common/tcpconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 2d7265f..853fe15 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -62,7 +62,8 @@ HEADERS += \ ip4over4.h \ ip4over6.h \ ip6over4.h \ - ip6over6.h + ip6over6.h \ + tcp.h HEADERS1 += \ ipv4addressdelegate.h \ @@ -71,7 +72,6 @@ HEADERS1 += \ gmp.h \ igmp.h \ mld.h \ - tcp.h \ udp.h \ textproto.h \ userscript.h \ @@ -97,14 +97,14 @@ SOURCES += \ snap.cpp \ arp.cpp \ ip4.cpp \ - ip6.cpp + ip6.cpp \ + tcp.cpp SOURCES1 += \ icmp.cpp \ gmp.cpp \ igmp.cpp \ mld.cpp \ - tcp.cpp \ udp.cpp \ textproto.cpp \ userscript.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 80fbab7..a848356 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -17,12 +17,12 @@ FORMS += \ snap.ui \ arp.ui \ ip4.ui \ - ip6.ui + ip6.ui \ + tcp.ui FORMS1 += \ icmp.ui \ gmp.ui \ - tcp.ui \ udp.ui \ textproto.ui \ userscript.ui \ @@ -61,7 +61,8 @@ HEADERS += \ arpconfig.h \ ip4config.h \ ip6config.h \ - ip4over4config.h + ip4over4config.h \ + tcpconfig.h SOURCES += \ ostprotolib.cpp \ @@ -84,7 +85,8 @@ SOURCES += \ snapconfig.cpp \ arpconfig.cpp \ ip4config.cpp \ - ip6config.cpp + ip6config.cpp \ + tcpconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 175864e..826eea3 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -25,7 +25,6 @@ along with this program. If not, see #include "icmp.h" #include "igmp.h" #include "mld.h" -#include "tcp.h" #include "udp.h" #include "textproto.h" #include "userscript.h" @@ -43,6 +42,7 @@ along with this program. If not, see #include "snap.h" #include "dot2snap.h" #include "eth2.h" +// L3 Protos #include "arp.h" #include "ip4.h" #include "ip6.h" @@ -50,6 +50,8 @@ along with this program. If not, see #include "ip4over6.h" #include "ip6over4.h" #include "ip6over6.h" +// L4 Protos +#include "tcp.h" #endif ProtocolManager *OstProtocolManager; @@ -66,8 +68,6 @@ ProtocolManager::ProtocolManager() (void*) IgmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kMldFieldNumber, (void*) MldProtocol::createInstance); - registerProtocol(OstProto::Protocol::kTcpFieldNumber, - (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, @@ -106,6 +106,7 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); + // Layer 3 Protocols registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, @@ -121,6 +122,10 @@ ProtocolManager::ProtocolManager() (void*) Ip6over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, (void*) Ip6over6Protocol::createInstance); + + // Layer 4 Protocols + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 1d7f5bb..aa878c0 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -37,6 +37,7 @@ along with this program. If not, see #include "ip4over6config.h" #include "ip6over4config.h" #include "ip6over6config.h" +#include "tcpconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -83,6 +84,7 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapConfigForm::createInstance); + // Layer 3 Protocols OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kArpFieldNumber, (void*) ArpConfigForm::createInstance); @@ -105,6 +107,11 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6over6FieldNumber, (void*) Ip6over6ConfigForm::createInstance); + + // Layer 4 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTcpFieldNumber, + (void*) TcpConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() diff --git a/common/tcp.cpp b/common/tcp.cpp index 39c71bf..2f9a5b4 100644 --- a/common/tcp.cpp +++ b/common/tcp.cpp @@ -17,26 +17,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "tcp.h" -TcpConfigForm::TcpConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } TcpProtocol::~TcpProtocol() { - delete configForm; } AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, @@ -614,96 +604,3 @@ int TcpProtocol::protocolFrameVariableCount() const return protocolFramePayloadVariableCount(); } -QWidget* TcpProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new TcpConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void TcpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->leTcpSrcPort->setText( - fieldData(tcp_src_port, FieldValue).toString()); - configForm->cbTcpSrcPortOverride->setChecked( - fieldData(tcp_is_override_src_port, FieldValue).toBool()); - - configForm->leTcpDstPort->setText( - fieldData(tcp_dst_port, FieldValue).toString()); - configForm->cbTcpDstPortOverride->setChecked( - fieldData(tcp_is_override_dst_port, FieldValue).toBool()); - - configForm->leTcpSeqNum->setText( - fieldData(tcp_seq_num, FieldValue).toString()); - configForm->leTcpAckNum->setText( - fieldData(tcp_ack_num, FieldValue).toString()); - - configForm->leTcpHdrLen->setText( - fieldData(tcp_hdrlen, FieldValue).toString()); - configForm->cbTcpHdrLenOverride->setChecked( - fieldData(tcp_is_override_hdrlen, FieldValue).toBool()); - - configForm->leTcpWindow->setText( - fieldData(tcp_window, FieldValue).toString()); - - configForm->leTcpCksum->setText(QString("%1").arg( - fieldData(tcp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbTcpCksumOverride->setChecked( - fieldData(tcp_is_override_cksum, FieldValue).toBool()); - - configForm->leTcpUrgentPointer->setText( - fieldData(tcp_urg_ptr, FieldValue).toString()); - - uint flags = fieldData(tcp_flags, FieldValue).toUInt(); - configForm->cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); - configForm->cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); - configForm->cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); - configForm->cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); - configForm->cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); - configForm->cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); -} - -void TcpProtocol::storeConfigWidget() -{ - bool isOk; - int ff = 0; - - configWidget(); - - setFieldData(tcp_src_port, configForm->leTcpSrcPort->text()); - setFieldData(tcp_is_override_src_port, - configForm->cbTcpSrcPortOverride->isChecked()); - setFieldData(tcp_dst_port, configForm->leTcpDstPort->text()); - setFieldData(tcp_is_override_dst_port, - configForm->cbTcpDstPortOverride->isChecked()); - - setFieldData(tcp_seq_num, configForm->leTcpSeqNum->text()); - setFieldData(tcp_ack_num, configForm->leTcpAckNum->text()); - - setFieldData(tcp_hdrlen, configForm->leTcpHdrLen->text()); - setFieldData(tcp_is_override_hdrlen, - configForm->cbTcpHdrLenOverride->isChecked()); - - setFieldData(tcp_window, configForm->leTcpWindow->text()); - - setFieldData(tcp_cksum, configForm->leTcpCksum->text().remove(QChar(' ')) - .toUInt(&isOk, BASE_HEX)); - setFieldData(tcp_is_override_cksum, - configForm->cbTcpCksumOverride->isChecked()); - - setFieldData(tcp_urg_ptr, configForm->leTcpUrgentPointer->text()); - - if (configForm->cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; - if (configForm->cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; - if (configForm->cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; - if (configForm->cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; - if (configForm->cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; - if (configForm->cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; - setFieldData(tcp_flags, ff); -} - diff --git a/common/tcp.h b/common/tcp.h index 9addef0..276ba65 100644 --- a/common/tcp.h +++ b/common/tcp.h @@ -23,7 +23,6 @@ along with this program. If not, see #include "abstractprotocol.h" #include "tcp.pb.h" -#include "ui_tcp.h" #define TCP_FLAG_URG 0x20 #define TCP_FLAG_ACK 0x10 @@ -32,18 +31,9 @@ along with this program. If not, see #define TCP_FLAG_SYN 0x02 #define TCP_FLAG_FIN 0x01 -class TcpConfigForm : public QWidget, public Ui::tcp -{ - Q_OBJECT -public: - TcpConfigForm(QWidget *parent = 0); -}; - class TcpProtocol : public AbstractProtocol { -private: - OstProto::Tcp data; - TcpConfigForm *configForm; +public: enum tcpfield { tcp_src_port = 0, @@ -65,7 +55,6 @@ private: tcp_fieldCount }; -public: TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~TcpProtocol(); @@ -93,9 +82,8 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Tcp data; }; #endif diff --git a/common/tcpconfig.cpp b/common/tcpconfig.cpp new file mode 100644 index 0000000..ff08f7d --- /dev/null +++ b/common/tcpconfig.cpp @@ -0,0 +1,175 @@ +/* +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 +*/ + +#include "tcpconfig.h" +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +TcpConfigForm::~TcpConfigForm() +{ +} + +TcpConfigForm* TcpConfigForm::createInstance() +{ + return new TcpConfigForm; +} + +void TcpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leTcpSrcPort->setText( + proto->fieldData( + TcpProtocol::tcp_src_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpSrcPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_src_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpDstPort->setText( + proto->fieldData( + TcpProtocol::tcp_dst_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpDstPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_dst_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpSeqNum->setText( + proto->fieldData( + TcpProtocol::tcp_seq_num, + AbstractProtocol::FieldValue + ).toString()); + leTcpAckNum->setText( + proto->fieldData( + TcpProtocol::tcp_ack_num, + AbstractProtocol::FieldValue + ).toString()); + + leTcpHdrLen->setText( + proto->fieldData( + TcpProtocol::tcp_hdrlen, + AbstractProtocol::FieldValue + ).toString()); + cbTcpHdrLenOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_hdrlen, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpWindow->setText( + proto->fieldData( + TcpProtocol::tcp_window, + AbstractProtocol::FieldValue + ).toString()); + + leTcpCksum->setText(uintToHexStr( + proto->fieldData( + TcpProtocol::tcp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbTcpCksumOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_cksum, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpUrgentPointer->setText( + proto->fieldData( + TcpProtocol::tcp_urg_ptr, + AbstractProtocol::FieldValue + ).toString()); + + uint flags = proto->fieldData( + TcpProtocol::tcp_flags, + AbstractProtocol::FieldValue + ).toUInt(); + + cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpConfigForm::storeWidget(AbstractProtocol *proto) +{ + int ff = 0; + + proto->setFieldData( + TcpProtocol::tcp_src_port, + leTcpSrcPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_src_port, + cbTcpSrcPortOverride->isChecked()); + proto->setFieldData( + TcpProtocol::tcp_dst_port, + leTcpDstPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_dst_port, + cbTcpDstPortOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_seq_num, + leTcpSeqNum->text()); + proto->setFieldData( + TcpProtocol::tcp_ack_num, + leTcpAckNum->text()); + + proto->setFieldData( + TcpProtocol::tcp_hdrlen, + leTcpHdrLen->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_hdrlen, + cbTcpHdrLenOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_window, + leTcpWindow->text()); + + proto->setFieldData( + TcpProtocol::tcp_cksum, + hexStrToUInt(leTcpCksum->text())); + proto->setFieldData( + TcpProtocol::tcp_is_override_cksum, + cbTcpCksumOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_urg_ptr, + leTcpUrgentPointer->text()); + + if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + + proto->setFieldData(TcpProtocol::tcp_flags, ff); +} + diff --git a/common/tcpconfig.h b/common/tcpconfig.h new file mode 100644 index 0000000..859238a --- /dev/null +++ b/common/tcpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TCP_CONFIG_H +#define _TCP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_tcp.h" + +class TcpConfigForm : + public AbstractProtocolConfigForm, + private Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); + virtual ~TcpConfigForm(); + + static TcpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif From 481e517fa646dcea9bd32dec0470eb2815e6a316 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 10 Apr 2014 18:24:30 +0530 Subject: [PATCH 229/294] NOX: GMP, IGMP, MLD - Separated protocol and widget as per new framework --- common/gmp.cpp | 288 +---------------------- common/gmp.h | 78 ++----- common/gmpconfig.cpp | 384 +++++++++++++++++++++++++++++++ common/gmpconfig.h | 63 +++++ common/igmp.cpp | 87 +------ common/igmp.h | 15 +- common/igmpconfig.cpp | 112 +++++++++ common/igmpconfig.h | 40 ++++ common/mld.cpp | 83 +------ common/mld.h | 15 +- common/mldconfig.cpp | 109 +++++++++ common/mldconfig.h | 41 ++++ common/ostproto.pro | 16 +- common/ostprotogui.pro | 10 +- common/protocolmanager.cpp | 12 +- common/protocolwidgetfactory.cpp | 10 + 16 files changed, 808 insertions(+), 555 deletions(-) create mode 100644 common/gmpconfig.cpp create mode 100644 common/gmpconfig.h create mode 100644 common/igmpconfig.cpp create mode 100644 common/igmpconfig.h create mode 100644 common/mldconfig.cpp create mode 100644 common/mldconfig.h diff --git a/common/gmp.cpp b/common/gmp.cpp index a7b4a3d..8d7b0fb 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -18,193 +18,17 @@ along with this program. If not, see */ #include "gmp.h" - -#include -#include +#include QHash GmpProtocol::frameFieldCountMap; -GmpConfigForm::GmpConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - auxData->setValidator(new QRegExpValidator( - QRegExp("[0-9A-Fa-f]*"), this)); -} - -GmpConfigForm::~GmpConfigForm() -{ -} - -void GmpConfigForm::update() -{ - // save the current group Record by simulating a currentItemChanged() - on_groupList_currentItemChanged(groupList->currentItem(), - groupList->currentItem()); -} - -void GmpConfigForm::on_groupMode_currentIndexChanged(int index) -{ - bool disabled = (index == 0); - - groupCount->setDisabled(disabled); - groupPrefix->setDisabled(disabled); -} - -void GmpConfigForm::on_addSource_clicked() -{ - QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); - item->setFlags(item->flags() | Qt::ItemIsEditable); - sourceList->insertItem(sourceList->currentRow(), item); - - if (!overrideSourceCount->isChecked()) - sourceCount->setText(QString().setNum(sourceList->count())); -} - -void GmpConfigForm::on_deleteSource_clicked() -{ - delete sourceList->takeItem(sourceList->currentRow()); - - if (!overrideSourceCount->isChecked()) - sourceCount->setText(QString().setNum(sourceList->count())); -} - -void GmpConfigForm::on_addGroupRecord_clicked() -{ - OstProto::Gmp::GroupRecord defRec; - QVariantMap grpRec; - QListWidgetItem *item = new QListWidgetItem; - - grpRec["groupRecordType"] = defRec.type(); - grpRec["groupRecordAddress"] = _defaultGroupIp; - grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); - grpRec["groupRecordSourceCount"] = defRec.source_count(); - grpRec["groupRecordSourceList"] = QStringList(); - grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); - grpRec["auxDataLength"] = defRec.aux_data_length(); - grpRec["auxData"] = QByteArray().append( - QString().fromStdString(defRec.aux_data())); - - item->setData(Qt::UserRole, grpRec); - item->setText(QString("%1: %2") - .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) - .arg(grpRec["groupRecordAddress"].toString())); - - groupList->insertItem(groupList->currentRow(), item); - - if (!overrideGroupRecordCount->isChecked()) - groupRecordCount->setText(QString().setNum(groupList->count())); -} - -void GmpConfigForm::on_deleteGroupRecord_clicked() -{ - delete groupList->takeItem(groupList->currentRow()); - - if (!overrideGroupRecordCount->isChecked()) - groupRecordCount->setText(QString().setNum(groupList->count())); -} - -void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, - QListWidgetItem *previous) -{ - QVariantMap rec; - QStringList strList; - - qDebug("in %s", __FUNCTION__); - - // save previous record ... - if (previous == NULL) - goto _load_current_record; - - rec["groupRecordType"] = groupRecordType->currentIndex(); - rec["groupRecordAddress"] = groupRecordAddress->text(); - strList.clear(); - while (groupRecordSourceList->count()) - { - QListWidgetItem *item = groupRecordSourceList->takeItem(0); - strList.append(item->text()); - delete item; - } - rec["groupRecordSourceList"] = strList; - rec["overrideGroupRecordSourceCount"] = - overrideGroupRecordSourceCount->isChecked(); - rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); - rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); - rec["auxDataLength"] = auxDataLength->text().toUInt(); - rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); - - previous->setData(Qt::UserRole, rec); - previous->setText(QString("%1: %2") - .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) - .arg(rec["groupRecordAddress"].toString())); - -_load_current_record: - // ... and load current record - if (current == NULL) - goto _exit; - - rec = current->data(Qt::UserRole).toMap(); - - groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); - groupRecordAddress->setText(rec["groupRecordAddress"].toString()); - strList = rec["groupRecordSourceList"].toStringList(); - groupRecordSourceList->clear(); - foreach (QString str, strList) - { - QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); - item->setFlags(item->flags() | Qt::ItemIsEditable); - } - overrideGroupRecordSourceCount->setChecked( - rec["overrideGroupRecordSourceCount"].toBool()); - groupRecordSourceCount->setText(QString().setNum( - rec["groupRecordSourceCount"].toUInt())); - overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); - auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); - auxData->setText(QString(rec["auxData"].toByteArray().toHex())); - -_exit: - groupRecord->setEnabled(current != NULL); - return; -} - -void GmpConfigForm::on_addGroupRecordSource_clicked() -{ - QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); - item->setFlags(item->flags() | Qt::ItemIsEditable); - groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); - - if (!overrideGroupRecordSourceCount->isChecked()) - groupRecordSourceCount->setText(QString().setNum( - groupRecordSourceList->count())); -} - -void GmpConfigForm::on_deleteGroupRecordSource_clicked() -{ - delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); - - if (!overrideGroupRecordSourceCount->isChecked()) - groupRecordSourceCount->setText(QString().setNum( - groupRecordSourceList->count())); -} - -void GmpConfigForm::on_auxData_textChanged(const QString &text) -{ - // auxDataLength is in units of words and each byte is 2 chars in text() - if (!overrideAuxDataLength->isChecked()) - auxDataLength->setText(QString().setNum((text.size()+7)/8)); -} - GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - /* The configWidget is created lazily */ - configForm = NULL; } GmpProtocol::~GmpProtocol() { - delete configForm; } AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const @@ -943,113 +767,3 @@ int GmpProtocol::protocolFrameVariableCount() const return count; } - -void GmpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); - // XXX: configForm->maxResponseTime set by subclass - configForm->overrideChecksum->setChecked( - fieldData(kIsOverrideChecksum, FieldValue).toBool()); - configForm->checksum->setText(uintToHexStr( - fieldData(kChecksum, FieldValue).toUInt(), 2)); - - configForm->groupAddress->setText( - fieldData(kGroupAddress, FieldValue).toString()); - configForm->groupMode->setCurrentIndex( - fieldData(kGroupMode, FieldValue).toUInt()); - configForm->groupCount->setText( - fieldData(kGroupCount, FieldValue).toString()); - configForm->groupPrefix->setText( - fieldData(kGroupPrefix, FieldValue).toString()); - - configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool()); - configForm->qrv->setText(fieldData(kQrv, FieldValue).toString()); - configForm->qqi->setText(fieldData(kQqic, FieldValue).toString()); - - QStringList sl = fieldData(kSources, FieldValue).toStringList(); - configForm->sourceList->clear(); - foreach(QString src, sl) - { - QListWidgetItem *item = new QListWidgetItem(src); - item->setFlags(item->flags() | Qt::ItemIsEditable); - configForm->sourceList->addItem(item); - } - - // NOTE: SourceCount should be loaded after sourceList - configForm->overrideSourceCount->setChecked( - fieldData(kIsOverrideSourceCount, FieldValue).toBool()); - configForm->sourceCount->setText( - fieldData(kSourceCount, FieldValue).toString()); - - QVariantList list = fieldData(kGroupRecords, FieldValue).toList(); - configForm->groupList->clear(); - foreach (QVariant rec, list) - { - QVariantMap grpRec = rec.toMap(); - QListWidgetItem *item = new QListWidgetItem; - - item->setData(Qt::UserRole, grpRec); - item->setText(QString("%1: %2") - .arg(configForm->groupRecordType->itemText( - grpRec["groupRecordType"].toInt())) - .arg(grpRec["groupRecordAddress"].toString())); - configForm->groupList->addItem(item); - } - - // NOTE: recordCount should be loaded after recordList - configForm->overrideGroupRecordCount->setChecked( - fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool()); - configForm->groupRecordCount->setText( - fieldData(kGroupRecordCount, FieldValue).toString()); - -} - -void GmpProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - configForm->update(); - - setFieldData(kType, configForm->msgTypeCombo->currentValue()); - // XXX: configForm->maxResponseTime handled by subclass - setFieldData(kIsOverrideChecksum, - configForm->overrideChecksum->isChecked()); - setFieldData(kChecksum, - configForm->checksum->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(kGroupAddress, configForm->groupAddress->text()); - setFieldData(kGroupMode, configForm->groupMode->currentIndex()); - setFieldData(kGroupCount, configForm->groupCount->text()); - setFieldData(kGroupPrefix, configForm->groupPrefix->text().remove('/')); - - setFieldData(kSFlag, configForm->sFlag->isChecked()); - setFieldData(kQrv, configForm->qrv->text()); - setFieldData(kQqic, configForm->qqi->text()); - - QStringList list; - for (int i = 0; i < configForm->sourceList->count(); i++) - list.append(configForm->sourceList->item(i)->text()); - setFieldData(kSources, list); - - // sourceCount should be AFTER sources - setFieldData(kIsOverrideSourceCount, - configForm->overrideSourceCount->isChecked()); - setFieldData(kSourceCount, configForm->sourceCount->text()); - - QVariantList grpList; - for (int i = 0; i < configForm->groupList->count(); i++) - { - QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole); - grpList.append(grp.toMap()); - } - setFieldData(kGroupRecords, grpList); - - // groupRecordCount should be AFTER groupRecords - setFieldData(kIsOverrideGroupRecordCount, - configForm->overrideGroupRecordCount->isChecked()); - setFieldData(kGroupRecordCount, configForm->groupRecordCount->text()); -} diff --git a/common/gmp.h b/common/gmp.h index 7f3fd29..a293c1f 100755 --- a/common/gmp.h +++ b/common/gmp.h @@ -20,72 +20,18 @@ along with this program. If not, see #ifndef _GMP_H #define _GMP_H -#include "gmp.pb.h" -#include "ui_gmp.h" - #include "abstractprotocol.h" +#include "gmp.pb.h" #include /* Gmp Protocol Frame Format - TODO: for now see the respective RFCs */ -class GmpProtocol; - -class GmpConfigForm : public QWidget, public Ui::Gmp -{ - Q_OBJECT -public: - GmpConfigForm(QWidget *parent = 0); - ~GmpConfigForm(); - void update(); -protected: - QString _defaultGroupIp; - QString _defaultSourceIp; - enum { - kSsmQueryPage = 0, - kSsmReportPage = 1 - }; -private slots: - void on_groupMode_currentIndexChanged(int index); - void on_addSource_clicked(); - void on_deleteSource_clicked(); - - void on_addGroupRecord_clicked(); - void on_deleteGroupRecord_clicked(); - void on_groupList_currentItemChanged(QListWidgetItem *current, - QListWidgetItem *previous); - void on_addGroupRecordSource_clicked(); - void on_deleteGroupRecordSource_clicked(); - void on_auxData_textChanged(const QString &text); -}; class GmpProtocol : public AbstractProtocol { public: - GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); - virtual ~GmpProtocol(); - - virtual ProtocolIdType protocolIdType() const; - - virtual int fieldCount() const; - virtual int frameFieldCount() const; - - virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; - virtual QVariant fieldData(int index, FieldAttrib attrib, - int streamIndex = 0) const; - virtual bool setFieldData(int index, const QVariant &value, - FieldAttrib attrib = FieldValue); - - virtual int protocolFrameSize(int streamIndex = 0) const; - - virtual bool isProtocolFrameValueVariable() const; - virtual int protocolFrameVariableCount() const; - - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); - -protected: enum GmpField { // ------------ @@ -134,8 +80,27 @@ protected: FIELD_COUNT }; + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +protected: OstProto::Gmp data; - GmpConfigForm *configForm; int msgType() const; @@ -146,6 +111,7 @@ protected: int qqic(int value) const; virtual quint16 checksum(int streamIndex) const = 0; + private: static QHash frameFieldCountMap; }; diff --git a/common/gmpconfig.cpp b/common/gmpconfig.cpp new file mode 100644 index 0000000..ee6af11 --- /dev/null +++ b/common/gmpconfig.cpp @@ -0,0 +1,384 @@ +/* +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 +*/ + +#include "gmpconfig.h" +#include "gmp.h" + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + msgTypeCombo->setValue( + proto->fieldData( + GmpProtocol::kType, + AbstractProtocol::FieldValue + ).toUInt()); + + // XXX: maxResponseTime set by subclass + + overrideChecksum->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideChecksum, + AbstractProtocol::FieldValue + ).toBool()); + checksum->setText(uintToHexStr( + proto->fieldData( + GmpProtocol::kChecksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + groupAddress->setText( + proto->fieldData( + GmpProtocol::kGroupAddress, + AbstractProtocol::FieldValue + ).toString()); + groupMode->setCurrentIndex( + proto->fieldData( + GmpProtocol::kGroupMode, + AbstractProtocol::FieldValue + ).toUInt()); + groupCount->setText( + proto->fieldData( + GmpProtocol::kGroupCount, + AbstractProtocol::FieldValue + ).toString()); + groupPrefix->setText( + proto->fieldData( + GmpProtocol::kGroupPrefix, + AbstractProtocol::FieldValue + ).toString()); + + sFlag->setChecked( + proto->fieldData( + GmpProtocol::kSFlag, + AbstractProtocol::FieldValue + ).toBool()); + qrv->setText( + proto->fieldData( + GmpProtocol::kQrv, + AbstractProtocol::FieldValue + ).toString()); + qqi->setText( + proto->fieldData( + GmpProtocol::kQqic, + AbstractProtocol::FieldValue + ).toString()); + + QStringList sl = + proto->fieldData( + GmpProtocol::kSources, + AbstractProtocol::FieldValue + ).toStringList(); + sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + overrideSourceCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideSourceCount, + AbstractProtocol::FieldValue + ).toBool()); + sourceCount->setText( + proto->fieldData( + GmpProtocol::kSourceCount, + AbstractProtocol::FieldValue + ).toString()); + + QVariantList list = + proto->fieldData( + GmpProtocol::kGroupRecords, + AbstractProtocol::FieldValue + ).toList(); + groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + overrideGroupRecordCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + AbstractProtocol::FieldValue + ).toBool()); + groupRecordCount->setText( + proto->fieldData( + GmpProtocol::kGroupRecordCount, + AbstractProtocol::FieldValue + ).toString()); +} + +void GmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + update(); + + proto->setFieldData( + GmpProtocol::kType, + msgTypeCombo->currentValue()); + + // XXX: maxResponseTime handled by subclass + + proto->setFieldData( + GmpProtocol::kIsOverrideChecksum, + overrideChecksum->isChecked()); + proto->setFieldData( + GmpProtocol::kChecksum, + hexStrToUInt(checksum->text())); + + proto->setFieldData( + GmpProtocol::kGroupAddress, + groupAddress->text()); + proto->setFieldData( + GmpProtocol::kGroupMode, + groupMode->currentIndex()); + proto->setFieldData( + GmpProtocol::kGroupCount, + groupCount->text()); + proto->setFieldData( + GmpProtocol::kGroupPrefix, + groupPrefix->text().remove('/')); + + proto->setFieldData( + GmpProtocol::kSFlag, + sFlag->isChecked()); + proto->setFieldData( + GmpProtocol::kQrv, + qrv->text()); + proto->setFieldData( + GmpProtocol::kQqic, + qqi->text()); + + QStringList list; + for (int i = 0; i < sourceList->count(); i++) + list.append(sourceList->item(i)->text()); + + proto->setFieldData( + GmpProtocol::kSources, + list); + + // sourceCount should be AFTER sources + proto->setFieldData( + GmpProtocol::kIsOverrideSourceCount, + overrideSourceCount->isChecked()); + proto->setFieldData( + GmpProtocol::kSourceCount, + sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < groupList->count(); i++) + { + QVariant grp = groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + proto->setFieldData(GmpProtocol::kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + proto->setFieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + overrideGroupRecordCount->isChecked()); + proto->setFieldData( + GmpProtocol::kGroupRecordCount, + groupRecordCount->text()); +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +// +// -- private slots +// + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} diff --git a/common/gmpconfig.h b/common/gmpconfig.h new file mode 100644 index 0000000..70a833b --- /dev/null +++ b/common/gmpconfig.h @@ -0,0 +1,63 @@ +/* +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 +*/ + +#ifndef _GMP_CONFIG_H +#define _GMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_gmp.h" + +class GmpConfigForm : + public AbstractProtocolConfigForm, + protected Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; + +private: + void update(); + +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +#endif diff --git a/common/igmp.cpp b/common/igmp.cpp index 046f675..8a5a0b9 100644 --- a/common/igmp.cpp +++ b/common/igmp.cpp @@ -18,68 +18,10 @@ along with this program. If not, see */ #include "igmp.h" - -#include "ipv4addressdelegate.h" #include "iputils.h" #include -#include - -IgmpConfigForm::IgmpConfigForm(QWidget *parent) - : GmpConfigForm(parent) -{ - connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), - SLOT(on_msgTypeCombo_currentIndexChanged(int))); - - msgTypeCombo->setValueMask(0xFF); - msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); - msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); - msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); - msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); - msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); - msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); - msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); - - _defaultGroupIp = "0.0.0.0"; - _defaultSourceIp = "0.0.0.0"; - - groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator - groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator - sourceList->setItemDelegate(new IPv4AddressDelegate(this)); - groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); -} - -void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) -{ - switch(msgTypeCombo->currentValue()) - { - case kIgmpV1Query: - case kIgmpV1Report: - case kIgmpV2Query: - case kIgmpV2Report: - case kIgmpV2Leave: - asmGroup->show(); - ssmWidget->hide(); - break; - - case kIgmpV3Query: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmQueryPage); - ssmWidget->show(); - break; - - case kIgmpV3Report: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmReportPage); - ssmWidget->show(); - break; - - default: - asmGroup->hide(); - ssmWidget->hide(); - break; - } -} +#include IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) @@ -403,33 +345,6 @@ _exit: return isOk; } -QWidget* IgmpProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new IgmpConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void IgmpProtocol::loadConfigWidget() -{ - GmpProtocol::loadConfigWidget(); - - configForm->maxResponseTime->setText( - fieldData(kRsvdMrtCode, FieldValue).toString()); -} - -void IgmpProtocol::storeConfigWidget() -{ - GmpProtocol::storeConfigWidget(); - - setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text()); -} - quint16 IgmpProtocol::checksum(int streamIndex) const { quint16 cks; diff --git a/common/igmp.h b/common/igmp.h index 6ee9ad3..0634a1f 100644 --- a/common/igmp.h +++ b/common/igmp.h @@ -19,8 +19,8 @@ along with this program. If not, see #ifndef _IGMP_H #define _IGMP_H -#include "igmp.pb.h" #include "gmp.h" +#include "igmp.pb.h" // IGMP uses the same msg type value for 'Query' messages across // versions despite the fields being different. To distinguish @@ -39,15 +39,6 @@ enum IgmpMsgType kIgmpV3Report = 0x22, }; -class IgmpConfigForm : public GmpConfigForm -{ - Q_OBJECT -public: - IgmpConfigForm(QWidget *parent = 0); -private slots: - void on_msgTypeCombo_currentIndexChanged(int index); -}; - class IgmpProtocol : public GmpProtocol { public: @@ -71,10 +62,6 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); - protected: virtual bool isSsmReport() const; virtual bool isQuery() const; diff --git a/common/igmpconfig.cpp b/common/igmpconfig.cpp new file mode 100644 index 0000000..d7bdaa4 --- /dev/null +++ b/common/igmpconfig.cpp @@ -0,0 +1,112 @@ +/* +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 +*/ + +#include "igmpconfig.h" +#include "igmp.h" +#include "ipv4addressdelegate.h" + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +IgmpConfigForm::~IgmpConfigForm() +{ +} + +IgmpConfigForm* IgmpConfigForm::createInstance() +{ + return new IgmpConfigForm; +} + +void IgmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + IgmpProtocol::kRsvdMrtCode, + AbstractProtocol::FieldValue + ).toString()); +} + +void IgmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + IgmpProtocol::kRsvdMrtCode, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + diff --git a/common/igmpconfig.h b/common/igmpconfig.h new file mode 100644 index 0000000..6428db1 --- /dev/null +++ b/common/igmpconfig.h @@ -0,0 +1,40 @@ +/* +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 +*/ +#ifndef _IGMP_CONFIG_H +#define _IGMP_CONFIG_H + +#include "gmpconfig.h" + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); + virtual ~IgmpConfigForm(); + + static IgmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/mld.cpp b/common/mld.cpp index 4c373ae..52b48a5 100644 --- a/common/mld.cpp +++ b/common/mld.cpp @@ -19,64 +19,10 @@ along with this program. If not, see #include "mld.h" -#include "ipv6addressdelegate.h" -#include "ipv6addressvalidator.h" #include "iputils.h" #include -#include - -MldConfigForm::MldConfigForm(QWidget *parent) - : GmpConfigForm(parent) -{ - connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), - SLOT(on_msgTypeCombo_currentIndexChanged(int))); - - msgTypeCombo->setValueMask(0xFF); - msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); - msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); - msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); - msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); - msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); - - _defaultGroupIp = "::"; - _defaultSourceIp = "::"; - - groupAddress->setValidator(new IPv6AddressValidator(this)); - groupRecordAddress->setValidator(new IPv6AddressValidator(this)); - sourceList->setItemDelegate(new IPv6AddressDelegate(this)); - groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); -} - -void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) -{ - switch(msgTypeCombo->currentValue()) - { - case kMldV1Query: - case kMldV1Report: - case kMldV1Done: - asmGroup->show(); - ssmWidget->hide(); - break; - - case kMldV2Query: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmQueryPage); - ssmWidget->show(); - break; - - case kMldV2Report: - asmGroup->hide(); - ssmWidget->setCurrentIndex(kSsmReportPage); - ssmWidget->show(); - break; - - default: - asmGroup->hide(); - ssmWidget->hide(); - break; - } -} +#include MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) @@ -591,33 +537,6 @@ _exit: return isOk; } -QWidget* MldProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new MldConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void MldProtocol::loadConfigWidget() -{ - GmpProtocol::loadConfigWidget(); - - configForm->maxResponseTime->setText( - fieldData(kMldMrt, FieldValue).toString()); -} - -void MldProtocol::storeConfigWidget() -{ - GmpProtocol::storeConfigWidget(); - - setFieldData(kMldMrt, configForm->maxResponseTime->text()); -} - quint16 MldProtocol::checksum(int streamIndex) const { return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); diff --git a/common/mld.h b/common/mld.h index bc2d95e..5403af6 100644 --- a/common/mld.h +++ b/common/mld.h @@ -19,8 +19,8 @@ along with this program. If not, see #ifndef _MLD_H #define _MLD_H -#include "mld.pb.h" #include "gmp.h" +#include "mld.pb.h" // MLD uses the same msg type value for 'Query' messages across // versions despite the fields being different. To distinguish @@ -36,15 +36,6 @@ enum MldMsgType kMldV2Report = 0x8F }; -class MldConfigForm : public GmpConfigForm -{ - Q_OBJECT -public: - MldConfigForm(QWidget *parent = 0); -private slots: - void on_msgTypeCombo_currentIndexChanged(int index); -}; - class MldProtocol : public GmpProtocol { public: @@ -69,10 +60,6 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); - protected: virtual bool isSsmReport() const; virtual bool isQuery() const; diff --git a/common/mldconfig.cpp b/common/mldconfig.cpp new file mode 100644 index 0000000..d6b7b4b --- /dev/null +++ b/common/mldconfig.cpp @@ -0,0 +1,109 @@ +/* +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 +*/ + +#include "mldconfig.h" +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +MldConfigForm::~MldConfigForm() +{ +} + +MldConfigForm* MldConfigForm::createInstance() +{ + return new MldConfigForm; +} + +void MldConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + MldProtocol::kMldMrt, + AbstractProtocol::FieldValue + ).toString()); +} + +void MldConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + MldProtocol::kMldMrt, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} diff --git a/common/mldconfig.h b/common/mldconfig.h new file mode 100644 index 0000000..b25617a --- /dev/null +++ b/common/mldconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ +#ifndef _MLD_CONFIG_H +#define _MLD_CONFIG_H + +#include "gmpconfig.h" + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); + + virtual ~MldConfigForm(); + + static MldConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 853fe15..2ef4deb 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -63,15 +63,13 @@ HEADERS += \ ip4over6.h \ ip6over4.h \ ip6over6.h \ - tcp.h - -HEADERS1 += \ - ipv4addressdelegate.h \ - ipv6addressdelegate.h \ - icmp.h \ gmp.h \ igmp.h \ mld.h \ + tcp.h + +HEADERS1 += \ + icmp.h \ udp.h \ textproto.h \ userscript.h \ @@ -98,13 +96,13 @@ SOURCES += \ arp.cpp \ ip4.cpp \ ip6.cpp \ + gmp.cpp \ + igmp.cpp \ + mld.cpp \ tcp.cpp SOURCES1 += \ icmp.cpp \ - gmp.cpp \ - igmp.cpp \ - mld.cpp \ udp.cpp \ textproto.cpp \ userscript.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index a848356..0759b2d 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -18,11 +18,11 @@ FORMS += \ arp.ui \ ip4.ui \ ip6.ui \ + gmp.ui \ tcp.ui FORMS1 += \ icmp.ui \ - gmp.ui \ udp.ui \ textproto.ui \ userscript.ui \ @@ -37,6 +37,8 @@ HEADERS = \ ostprotolib.h \ abstractfileformat.h \ fileformat.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ pcapfileformat.h \ pdmlfileformat.h \ pdmlprotocol.h \ @@ -62,6 +64,9 @@ HEADERS += \ ip4config.h \ ip6config.h \ ip4over4config.h \ + gmpconfig.h \ + igmpconfig.h \ + mldconfig.h \ tcpconfig.h SOURCES += \ @@ -86,6 +91,9 @@ SOURCES += \ arpconfig.cpp \ ip4config.cpp \ ip6config.cpp \ + gmpconfig.cpp \ + igmpconfig.cpp \ + mldconfig.cpp \ tcpconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 826eea3..428616c 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -23,8 +23,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 #include "icmp.h" -#include "igmp.h" -#include "mld.h" #include "udp.h" #include "textproto.h" #include "userscript.h" @@ -51,6 +49,8 @@ along with this program. If not, see #include "ip6over4.h" #include "ip6over6.h" // L4 Protos +#include "igmp.h" +#include "mld.h" #include "tcp.h" #endif @@ -64,10 +64,6 @@ ProtocolManager::ProtocolManager() #if 0 registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kIgmpFieldNumber, - (void*) IgmpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kMldFieldNumber, - (void*) MldProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, @@ -124,6 +120,10 @@ ProtocolManager::ProtocolManager() (void*) Ip6over6Protocol::createInstance); // Layer 4 Protocols + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, (void*) TcpProtocol::createInstance); #endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index aa878c0..a5ce20f 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -30,6 +30,7 @@ along with this program. If not, see #include "dot2llcconfig.h" #include "snapconfig.h" #include "dot2snapconfig.h" +// L3 Protocol Widgets #include "arpconfig.h" #include "ip4config.h" #include "ip6config.h" @@ -37,6 +38,9 @@ along with this program. If not, see #include "ip4over6config.h" #include "ip6over4config.h" #include "ip6over6config.h" +// L4 Protocol Widgets +#include "igmpconfig.h" +#include "mldconfig.h" #include "tcpconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -109,6 +113,12 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() (void*) Ip6over6ConfigForm::createInstance); // Layer 4 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMldFieldNumber, + (void*) MldConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kTcpFieldNumber, (void*) TcpConfigForm::createInstance); From bbe645725a45990ca3bf2ee1513edc7e70a1e894 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 15 Apr 2014 05:31:28 +0530 Subject: [PATCH 230/294] NOX: Udp - separated protocol and widget as per new framework --- common/ostproto.pro | 8 +-- common/ostprotogui.pro | 10 +-- common/protocolmanager.cpp | 6 +- common/protocolwidgetfactory.cpp | 4 ++ common/udp.cpp | 69 ------------------- common/udp.h | 19 +----- common/udpconfig.cpp | 114 +++++++++++++++++++++++++++++++ common/udpconfig.h | 41 +++++++++++ 8 files changed, 175 insertions(+), 96 deletions(-) create mode 100644 common/udpconfig.cpp create mode 100644 common/udpconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 2ef4deb..40a5b1d 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -66,11 +66,11 @@ HEADERS += \ gmp.h \ igmp.h \ mld.h \ - tcp.h + tcp.h \ + udp.h HEADERS1 += \ icmp.h \ - udp.h \ textproto.h \ userscript.h \ hexdump.h \ @@ -99,11 +99,11 @@ SOURCES += \ gmp.cpp \ igmp.cpp \ mld.cpp \ - tcp.cpp + tcp.cpp \ + udp.cpp SOURCES1 += \ icmp.cpp \ - udp.cpp \ textproto.cpp \ userscript.cpp \ hexdump.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 0759b2d..443ed02 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -19,11 +19,11 @@ FORMS += \ ip4.ui \ ip6.ui \ gmp.ui \ - tcp.ui + tcp.ui \ + udp.ui FORMS1 += \ icmp.ui \ - udp.ui \ textproto.ui \ userscript.ui \ hexdump.ui \ @@ -67,7 +67,8 @@ HEADERS += \ gmpconfig.h \ igmpconfig.h \ mldconfig.h \ - tcpconfig.h + tcpconfig.h \ + udpconfig.h SOURCES += \ ostprotolib.cpp \ @@ -94,7 +95,8 @@ SOURCES += \ gmpconfig.cpp \ igmpconfig.cpp \ mldconfig.cpp \ - tcpconfig.cpp + tcpconfig.cpp \ + udpconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 428616c..302cc96 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -23,7 +23,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 #include "icmp.h" -#include "udp.h" #include "textproto.h" #include "userscript.h" #include "hexdump.h" @@ -52,6 +51,7 @@ along with this program. If not, see #include "igmp.h" #include "mld.h" #include "tcp.h" +#include "udp.h" #endif ProtocolManager *OstProtocolManager; @@ -64,8 +64,6 @@ ProtocolManager::ProtocolManager() #if 0 registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kUdpFieldNumber, - (void*) UdpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); @@ -126,6 +124,8 @@ ProtocolManager::ProtocolManager() (void*) MldProtocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index a5ce20f..06ac075 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -42,6 +42,7 @@ along with this program. If not, see #include "igmpconfig.h" #include "mldconfig.h" #include "tcpconfig.h" +#include "udpconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -122,6 +123,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kTcpFieldNumber, (void*) TcpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUdpFieldNumber, + (void*) UdpConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() diff --git a/common/udp.cpp b/common/udp.cpp index 5a4e14b..f3d051c 100644 --- a/common/udp.cpp +++ b/common/udp.cpp @@ -17,26 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include -#include - #include "udp.h" -UdpConfigForm::UdpConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } UdpProtocol::~UdpProtocol() { - delete configForm; } AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, @@ -440,61 +429,3 @@ int UdpProtocol::protocolFrameVariableCount() const return protocolFramePayloadVariableCount(); } - -QWidget* UdpProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new UdpConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void UdpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->leUdpSrcPort->setText( - fieldData(udp_srcPort, FieldValue).toString()); - configForm->cbUdpSrcPortOverride->setChecked( - fieldData(udp_isOverrideSrcPort, FieldValue).toBool()); - configForm->leUdpDstPort->setText( - fieldData(udp_dstPort, FieldValue).toString()); - configForm->cbUdpDstPortOverride->setChecked( - fieldData(udp_isOverrideDstPort, FieldValue).toBool()); - - configForm->leUdpLength->setText( - fieldData(udp_totLen, FieldValue).toString()); - configForm->cbUdpLengthOverride->setChecked( - fieldData(udp_isOverrideTotLen, FieldValue).toBool()); - - configForm->leUdpCksum->setText(QString("%1").arg( - fieldData(udp_cksum, FieldValue).toUInt(), 4, BASE_HEX, QChar('0'))); - configForm->cbUdpCksumOverride->setChecked( - fieldData(udp_isOverrideCksum, FieldValue).toBool()); -} - -void UdpProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(udp_srcPort, configForm->leUdpSrcPort->text()); - setFieldData(udp_isOverrideSrcPort, - configForm->cbUdpSrcPortOverride->isChecked()); - setFieldData(udp_dstPort, configForm->leUdpDstPort->text()); - setFieldData(udp_isOverrideDstPort, - configForm->cbUdpDstPortOverride->isChecked()); - - setFieldData(udp_totLen, configForm->leUdpLength->text()); - setFieldData(udp_isOverrideTotLen, - configForm->cbUdpLengthOverride->isChecked()); - - setFieldData(udp_cksum, configForm->leUdpCksum->text().remove(QChar(' ')) - .toUInt(&isOk, BASE_HEX)); - setFieldData(udp_isOverrideCksum, - configForm->cbUdpCksumOverride->isChecked()); -} - diff --git a/common/udp.h b/common/udp.h index 7bdf200..56285b4 100644 --- a/common/udp.h +++ b/common/udp.h @@ -21,22 +21,11 @@ along with this program. If not, see #define _UDP_H #include "abstractprotocol.h" - #include "udp.pb.h" -#include "ui_udp.h" - -class UdpConfigForm : public QWidget, public Ui::udp -{ - Q_OBJECT -public: - UdpConfigForm(QWidget *parent = 0); -}; class UdpProtocol : public AbstractProtocol { -private: - OstProto::Udp data; - UdpConfigForm *configForm; +public: enum udpfield { udp_srcPort = 0, @@ -52,7 +41,6 @@ private: udp_fieldCount }; -public: UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~UdpProtocol(); @@ -80,9 +68,8 @@ public: virtual bool isProtocolFrameValueVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Udp data; }; #endif diff --git a/common/udpconfig.cpp b/common/udpconfig.cpp new file mode 100644 index 0000000..dab05ec --- /dev/null +++ b/common/udpconfig.cpp @@ -0,0 +1,114 @@ +/* +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 +*/ + +#include "udpconfig.h" +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +UdpConfigForm::~UdpConfigForm() +{ +} + +UdpConfigForm* UdpConfigForm::createInstance() +{ + return new UdpConfigForm; +} + + +void UdpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leUdpSrcPort->setText( + proto->fieldData( + UdpProtocol::udp_srcPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpSrcPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideSrcPort, + AbstractProtocol::FieldValue + ).toBool()); + leUdpDstPort->setText( + proto->fieldData( + UdpProtocol::udp_dstPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpDstPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideDstPort, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpLength->setText( + proto->fieldData( + UdpProtocol::udp_totLen, + AbstractProtocol::FieldValue + ).toString()); + cbUdpLengthOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpCksum->setText(uintToHexStr( + proto->fieldData( + UdpProtocol::udp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbUdpCksumOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); +} + +void UdpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UdpProtocol::udp_srcPort, + leUdpSrcPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideSrcPort, + cbUdpSrcPortOverride->isChecked()); + proto->setFieldData( + UdpProtocol::udp_dstPort, + leUdpDstPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideDstPort, + cbUdpDstPortOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_totLen, + leUdpLength->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideTotLen, + cbUdpLengthOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_cksum, + hexStrToUInt(leUdpCksum->text())); + proto->setFieldData( + UdpProtocol::udp_isOverrideCksum, + cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udpconfig.h b/common/udpconfig.h new file mode 100644 index 0000000..98b8ecb --- /dev/null +++ b/common/udpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _UDP_CONFIG_H +#define _UDP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_udp.h" + +class UdpConfigForm : + public AbstractProtocolConfigForm, + private Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); + virtual ~UdpConfigForm(); + + static UdpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif From 30c70a73e44f036734b478d5a637a7da0b26e66c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 17 Apr 2014 07:10:13 +0530 Subject: [PATCH 231/294] NOX: ICMP - Separated protocol and widget as per new framework --- common/icmp.cpp | 200 +------------------------------ common/icmp.h | 49 +++----- common/icmpconfig.cpp | 191 +++++++++++++++++++++++++++++ common/icmpconfig.h | 50 ++++++++ common/icmphelper.h | 88 ++++++++++++++ common/ostproto.pro | 4 +- common/ostprotogui.pro | 4 +- common/protocolmanager.cpp | 6 +- common/protocolwidgetfactory.cpp | 4 + 9 files changed, 356 insertions(+), 240 deletions(-) create mode 100644 common/icmpconfig.cpp create mode 100644 common/icmpconfig.h create mode 100644 common/icmphelper.h diff --git a/common/icmp.cpp b/common/icmp.cpp index e7f6267..651efb5 100644 --- a/common/icmp.cpp +++ b/common/icmp.cpp @@ -17,165 +17,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ - #include "icmp.h" - -#include -#include - -enum IcmpType -{ - kIcmpEchoReply = 0, - kIcmpDestinationUnreachable = 3, - kIcmpSourceQuench = 4, - kIcmpRedirect = 5, - kIcmpEchoRequest = 8, - kIcmpTimeExceeded = 11, - kIcmpParameterProblem = 12, - kIcmpTimestampRequest = 13, - kIcmpTimestampReply = 14, - kIcmpInformationRequest = 15, - kIcmpInformationReply = 16, - kIcmpAddressMaskRequest = 17, - kIcmpAddressMaskReply = 18 -}; - -enum Icmp6Type -{ - kIcmp6DestinationUnreachable = 1, - kIcmp6PacketTooBig = 2, - kIcmp6TimeExceeded = 3, - kIcmp6ParameterProblem = 4, - kIcmp6EchoRequest = 128, - kIcmp6EchoReply = 129, - kIcmp6RouterSolicitation = 133, - kIcmp6RouterAdvertisement = 134, - kIcmp6NeighbourSolicitation = 135, - kIcmp6NeighbourAdvertisement = 136, - kIcmp6Redirect = 137, - kIcmp6InformationQuery = 139, - kIcmp6InformationResponse = 140 -}; - -static QSet icmpIdSeqSet = QSet() - << kIcmpEchoRequest - << kIcmpEchoReply - << kIcmpInformationRequest - << kIcmpInformationReply; - -static QSet icmp6IdSeqSet = QSet() - << kIcmp6EchoRequest - << kIcmp6EchoReply; - -static bool isIdSeqType(OstProto::Icmp::Version ver, int type) -{ - //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); - switch(ver) - { - case OstProto::Icmp::kIcmp4: - return icmpIdSeqSet.contains(type); - case OstProto::Icmp::kIcmp6: - return icmp6IdSeqSet.contains(type); - default: - break; - } - - Q_ASSERT(false); // unreachable - return false; -} - -IcmpConfigForm::IcmpConfigForm(QWidget *parent) - : QWidget(parent) -{ - versionGroup = new QButtonGroup(this); - setupUi(this); - - // auto-connect's not working, for some reason I can't figure out! - // slot name changed to when_ instead of on_ so that connectSlotsByName() - // doesn't complain - connect(versionGroup, - SIGNAL(buttonClicked(int)), - SLOT(when_versionGroup_buttonClicked(int))); - - versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); - versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); - - typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); - - icmp4Button->click(); - - idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); - seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); -} - -void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) -{ - idSeqFrame->setVisible( - isIdSeqType( - OstProto::Icmp::Version(versionGroup->checkedId()), - typeCombo->currentValue())); -} - -void IcmpConfigForm::when_versionGroup_buttonClicked(int id) -{ - int value = typeCombo->currentValue(); - - typeCombo->clear(); - - switch(id) - { - case OstProto::Icmp::kIcmp4: - typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); - typeCombo->addItem(kIcmpDestinationUnreachable, - "Destination Unreachable"); - typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); - typeCombo->addItem(kIcmpRedirect, "Redirect"); - typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); - typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); - typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); - typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); - typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); - typeCombo->addItem(kIcmpInformationRequest, "Information Request"); - typeCombo->addItem(kIcmpInformationReply, "Information Reply"); - typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); - typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); - break; - - case OstProto::Icmp::kIcmp6: - typeCombo->addItem(kIcmp6DestinationUnreachable, - "Destination Unreachable"); - typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); - typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); - typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); - - typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); - typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); - typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); - typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); - typeCombo->addItem(kIcmp6NeighbourSolicitation, - "Neighbour Solicitation"); - typeCombo->addItem(kIcmp6NeighbourAdvertisement, - "Neighbour Advertisement"); - typeCombo->addItem(kIcmp6Redirect, "Redirect"); - typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); - typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); - break; - default: - Q_ASSERT(false); - } - - typeCombo->setValue(value); -} +#include "icmphelper.h" IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - configForm = NULL; } IcmpProtocol::~IcmpProtocol() { - delete configForm; } AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, @@ -541,54 +392,5 @@ _exit: return isOk; } -QWidget* IcmpProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new IcmpConfigForm; - loadConfigWidget(); - } - return configForm; -} - -void IcmpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->versionGroup->button(icmpVersion())->click(); - - configForm->typeCombo->setValue(fieldData(icmp_type, FieldValue).toUInt()); - configForm->codeEdit->setText(fieldData(icmp_code, FieldValue).toString()); - - configForm->overrideCksum->setChecked( - fieldData(icmp_is_override_checksum, FieldValue).toBool()); - configForm->cksumEdit->setText(uintToHexStr( - fieldData(icmp_checksum, FieldValue).toUInt(), 2)); - - configForm->idEdit->setText( - fieldData(icmp_identifier, FieldValue).toString()); - configForm->seqEdit->setText( - fieldData(icmp_sequence, FieldValue).toString()); - -} - -void IcmpProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - - setFieldData(icmp_version, configForm->versionGroup->checkedId()); - - setFieldData(icmp_type, configForm->typeCombo->currentValue()); - setFieldData(icmp_code, configForm->codeEdit->text()); - - setFieldData(icmp_is_override_checksum, - configForm->overrideCksum->isChecked()); - setFieldData(icmp_checksum, configForm->cksumEdit->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(icmp_identifier, configForm->idEdit->text()); - setFieldData(icmp_sequence, configForm->seqEdit->text()); -} diff --git a/common/icmp.h b/common/icmp.h index a3fc296..b24f025 100644 --- a/common/icmp.h +++ b/common/icmp.h @@ -20,12 +20,8 @@ along with this program. If not, see #ifndef _ICMP_H #define _ICMP_H -#include "icmp.pb.h" -#include "ui_icmp.h" - #include "abstractprotocol.h" - -#include +#include "icmp.pb.h" /* Icmp Protocol Frame Format - @@ -37,23 +33,9 @@ Fields within [] are applicable only to certain TYPEs Figures in braces represent field width in bytes */ -class IcmpConfigForm : public QWidget, public Ui::Icmp -{ - Q_OBJECT -public: - QButtonGroup *versionGroup; - - IcmpConfigForm(QWidget *parent = 0); -private slots: - void on_typeCombo_currentIndexChanged(int index); - void when_versionGroup_buttonClicked(int id); -}; - class IcmpProtocol : public AbstractProtocol { -private: - OstProto::Icmp data; - IcmpConfigForm *configForm; +public: enum icmpfield { // Frame Fields @@ -73,18 +55,6 @@ private: icmp_fieldCount }; - OstProto::Icmp::Version icmpVersion() const - { - return OstProto::Icmp::Version( - fieldData(icmp_version, FieldValue).toUInt()); - } - - int icmpType() const - { - return fieldData(icmp_type, FieldValue).toInt(); - } - -public: IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~IcmpProtocol(); @@ -109,9 +79,18 @@ public: virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Icmp data; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } }; #endif diff --git a/common/icmpconfig.cpp b/common/icmpconfig.cpp new file mode 100644 index 0000000..2bf65af --- /dev/null +++ b/common/icmpconfig.cpp @@ -0,0 +1,191 @@ +/* +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 +*/ + +#include "icmpconfig.h" + +#include "icmp.h" +#include "icmphelper.h" + +#include + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +IcmpConfigForm::~IcmpConfigForm() +{ +} + +IcmpConfigForm* IcmpConfigForm::createInstance() +{ + return new IcmpConfigForm; +} + +void IcmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + versionGroup->button( + proto->fieldData( + IcmpProtocol::icmp_version, + AbstractProtocol::FieldValue + ).toUInt())->click(); + + typeCombo->setValue( + proto->fieldData( + IcmpProtocol::icmp_type, + AbstractProtocol::FieldValue + ).toUInt()); + codeEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_code, + AbstractProtocol::FieldValue + ).toString()); + + overrideCksum->setChecked( + proto->fieldData( + IcmpProtocol::icmp_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + cksumEdit->setText(uintToHexStr( + proto->fieldData( + IcmpProtocol::icmp_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + idEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_identifier, + AbstractProtocol::FieldValue + ).toString()); + seqEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_sequence, + AbstractProtocol::FieldValue + ).toString()); +} + +void IcmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + IcmpProtocol::icmp_version, + versionGroup->checkedId()); + + proto->setFieldData( + IcmpProtocol::icmp_type, + typeCombo->currentValue()); + proto->setFieldData( + IcmpProtocol::icmp_code, + codeEdit->text()); + + proto->setFieldData( + IcmpProtocol::icmp_is_override_checksum, + overrideCksum->isChecked()); + proto->setFieldData( + IcmpProtocol::icmp_checksum, + hexStrToUInt(cksumEdit->text())); + + proto->setFieldData( + IcmpProtocol::icmp_identifier, + idEdit->text()); + proto->setFieldData( + IcmpProtocol::icmp_sequence, + seqEdit->text()); +} + +// +// -------- private slots +// +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + diff --git a/common/icmpconfig.h b/common/icmpconfig.h new file mode 100644 index 0000000..6e01065 --- /dev/null +++ b/common/icmpconfig.h @@ -0,0 +1,50 @@ +/* +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 +*/ + +#ifndef _ICMP_CONFIG_H +#define _ICMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_icmp.h" + +class QButtonGroup; + +class IcmpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Icmp +{ + Q_OBJECT +public: + IcmpConfigForm(QWidget *parent = 0); + virtual ~IcmpConfigForm(); + + static IcmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + QButtonGroup *versionGroup; + +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +#endif diff --git a/common/icmphelper.h b/common/icmphelper.h new file mode 100644 index 0000000..0c5bcf7 --- /dev/null +++ b/common/icmphelper.h @@ -0,0 +1,88 @@ +/* +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 +*/ + +#ifndef _ICMP_HELPER_H +#define _ICMP_HELPER_H + +#include "icmp.pb.h" + +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +bool inline isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index 40a5b1d..2dbe0c1 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -64,13 +64,13 @@ HEADERS += \ ip6over4.h \ ip6over6.h \ gmp.h \ + icmp.h \ igmp.h \ mld.h \ tcp.h \ udp.h HEADERS1 += \ - icmp.h \ textproto.h \ userscript.h \ hexdump.h \ @@ -97,13 +97,13 @@ SOURCES += \ ip4.cpp \ ip6.cpp \ gmp.cpp \ + icmp.cpp \ igmp.cpp \ mld.cpp \ tcp.cpp \ udp.cpp SOURCES1 += \ - icmp.cpp \ textproto.cpp \ userscript.cpp \ hexdump.cpp \ diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 443ed02..e8a6909 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -19,11 +19,11 @@ FORMS += \ ip4.ui \ ip6.ui \ gmp.ui \ + icmp.ui \ tcp.ui \ udp.ui FORMS1 += \ - icmp.ui \ textproto.ui \ userscript.ui \ hexdump.ui \ @@ -65,6 +65,7 @@ HEADERS += \ ip6config.h \ ip4over4config.h \ gmpconfig.h \ + icmpconfig.h \ igmpconfig.h \ mldconfig.h \ tcpconfig.h \ @@ -93,6 +94,7 @@ SOURCES += \ ip4config.cpp \ ip6config.cpp \ gmpconfig.cpp \ + icmpconfig.cpp \ igmpconfig.cpp \ mldconfig.cpp \ tcpconfig.cpp \ diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 302cc96..db1a4ca 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "icmp.h" #include "textproto.h" #include "userscript.h" #include "hexdump.h" @@ -48,6 +47,7 @@ along with this program. If not, see #include "ip6over4.h" #include "ip6over6.h" // L4 Protos +#include "icmp.h" #include "igmp.h" #include "mld.h" #include "tcp.h" @@ -62,8 +62,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kIcmpFieldNumber, - (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); @@ -118,6 +116,8 @@ ProtocolManager::ProtocolManager() (void*) Ip6over6Protocol::createInstance); // Layer 4 Protocols + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIgmpFieldNumber, (void*) IgmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kMldFieldNumber, diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 06ac075..78347ad 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -39,6 +39,7 @@ along with this program. If not, see #include "ip6over4config.h" #include "ip6over6config.h" // L4 Protocol Widgets +#include "icmpconfig.h" #include "igmpconfig.h" #include "mldconfig.h" #include "tcpconfig.h" @@ -114,6 +115,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() (void*) Ip6over6ConfigForm::createInstance); // Layer 4 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIgmpFieldNumber, (void*) IgmpConfigForm::createInstance); From bacee5dd1817402897d38d8b9fbb819f49be3182 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 19 Apr 2014 12:36:14 +0530 Subject: [PATCH 232/294] NOX: TextProtocol - Separated protocol and widget as per new framework --- common/ostproto.pro | 8 +-- common/ostprotogui.pro | 10 ++-- common/protocolmanager.cpp | 11 +++-- common/protocolwidgetfactory.cpp | 7 +++ common/textproto.cpp | 55 --------------------- common/textproto.h | 22 ++------- common/textproto.ui | 4 +- common/textprotoconfig.cpp | 84 ++++++++++++++++++++++++++++++++ common/textprotoconfig.h | 41 ++++++++++++++++ 9 files changed, 155 insertions(+), 87 deletions(-) create mode 100644 common/textprotoconfig.cpp create mode 100644 common/textprotoconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 2dbe0c1..c69e4e0 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -68,10 +68,10 @@ HEADERS += \ igmp.h \ mld.h \ tcp.h \ - udp.h + udp.h \ + textproto.h HEADERS1 += \ - textproto.h \ userscript.h \ hexdump.h \ sample.h @@ -101,10 +101,10 @@ SOURCES += \ igmp.cpp \ mld.cpp \ tcp.cpp \ - udp.cpp + udp.cpp \ + textproto.cpp SOURCES1 += \ - textproto.cpp \ userscript.cpp \ hexdump.cpp \ sample.cpp diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index e8a6909..98596bc 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -21,10 +21,10 @@ FORMS += \ gmp.ui \ icmp.ui \ tcp.ui \ - udp.ui + udp.ui \ + textproto.ui FORMS1 += \ - textproto.ui \ userscript.ui \ hexdump.ui \ sample.ui @@ -69,7 +69,8 @@ HEADERS += \ igmpconfig.h \ mldconfig.h \ tcpconfig.h \ - udpconfig.h + udpconfig.h \ + textprotoconfig.h SOURCES += \ ostprotolib.cpp \ @@ -98,7 +99,8 @@ SOURCES += \ igmpconfig.cpp \ mldconfig.cpp \ tcpconfig.cpp \ - udpconfig.cpp + udpconfig.cpp \ + textprotoconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index db1a4ca..8649dc9 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "textproto.h" #include "userscript.h" #include "hexdump.h" #include "sample.h" @@ -52,6 +51,8 @@ along with this program. If not, see #include "mld.h" #include "tcp.h" #include "udp.h" +// L5 Protos +#include "textproto.h" #endif ProtocolManager *OstProtocolManager; @@ -62,9 +63,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, - (void*) TextProtocol::createInstance); - registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpProtocol::createInstance); @@ -126,6 +124,11 @@ ProtocolManager::ProtocolManager() (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); + + // Layer 5 Protocols + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 78347ad..8c59eb4 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -44,6 +44,8 @@ along with this program. If not, see #include "mldconfig.h" #include "tcpconfig.h" #include "udpconfig.h" +// L5 Protocol Widgets +#include "textprotoconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -130,6 +132,11 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kUdpFieldNumber, (void*) UdpConfigForm::createInstance); + + // Layer 5 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocolConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() diff --git a/common/textproto.cpp b/common/textproto.cpp index 9834530..285668d 100644 --- a/common/textproto.cpp +++ b/common/textproto.cpp @@ -17,32 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - #include "textproto.h" -TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); - portNumCombo->addItem(0, "Reserved"); - portNumCombo->addItem(80, "HTTP"); - portNumCombo->addItem(554, "RTSP"); - portNumCombo->addItem(5060, "SIP"); -} - TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - /* The configWidget is created lazily */ - configForm = NULL; } TextProtocol::~TextProtocol() { - delete configForm; } AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, @@ -255,41 +238,3 @@ int TextProtocol::protocolFrameSize(int streamIndex) const return fieldData(textProto_text, FieldFrameValue, streamIndex) .toByteArray().size() ; } - -QWidget* TextProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new TextProtocolConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void TextProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->portNumCombo->setValue( - fieldData(textProto_portNum, FieldValue).toUInt()); - configForm->eolCombo->setCurrentIndex( - fieldData(textProto_eol, FieldValue).toUInt()); - configForm->encodingCombo->setCurrentIndex( - fieldData(textProto_encoding, FieldValue).toUInt()); - configForm->protoText->setText( - fieldData(textProto_text, FieldValue).toString()); -} - -void TextProtocol::storeConfigWidget() -{ - configWidget(); - - setFieldData(textProto_portNum, configForm->portNumCombo->currentValue()); - setFieldData(textProto_eol, configForm->eolCombo->currentIndex()); - setFieldData(textProto_encoding, configForm->encodingCombo->currentIndex()); - - setFieldData(textProto_text, configForm->protoText->toPlainText()); -} - diff --git a/common/textproto.h b/common/textproto.h index 1ec5fc0..8c00e47 100644 --- a/common/textproto.h +++ b/common/textproto.h @@ -20,10 +20,8 @@ along with this program. If not, see #ifndef _TEXT_PROTOCOL_H #define _TEXT_PROTOCOL_H -#include "textproto.pb.h" -#include "ui_textproto.h" - #include "abstractprotocol.h" +#include "textproto.pb.h" /* TextProtocol Protocol Frame Format - @@ -31,19 +29,9 @@ TextProtocol Protocol Frame Format - specified encoding */ -class TextProtocolConfigForm : public QWidget, public Ui::TextProtocol -{ - Q_OBJECT -public: - TextProtocolConfigForm(QWidget *parent = 0); -private slots: -}; - class TextProtocol : public AbstractProtocol { -private: - OstProto::TextProtocol data; - TextProtocolConfigForm *configForm; +public: enum textProtocolField { // Frame Fields @@ -57,7 +45,6 @@ private: textProto_fieldCount }; -public: TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~TextProtocol(); @@ -83,9 +70,8 @@ public: virtual int protocolFrameSize(int streamIndex = 0) const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::TextProtocol data; }; #endif diff --git a/common/textproto.ui b/common/textproto.ui index f6996aa..6e7ebdb 100644 --- a/common/textproto.ui +++ b/common/textproto.ui @@ -1,6 +1,6 @@ - TextProtocol - + TextProto + 0 diff --git a/common/textprotoconfig.cpp b/common/textprotoconfig.cpp new file mode 100644 index 0000000..0242ffa --- /dev/null +++ b/common/textprotoconfig.cpp @@ -0,0 +1,84 @@ +/* +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 +*/ + +#include "textprotoconfig.h" +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocolConfigForm::~TextProtocolConfigForm() +{ +} + +TextProtocolConfigForm* TextProtocolConfigForm::createInstance() +{ + return new TextProtocolConfigForm; +} + +void TextProtocolConfigForm::loadWidget(AbstractProtocol *proto) +{ + portNumCombo->setValue( + proto->fieldData( + TextProtocol::textProto_portNum, + AbstractProtocol::FieldValue + ).toUInt()); + eolCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_eol, + AbstractProtocol::FieldValue + ).toUInt()); + encodingCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_encoding, + AbstractProtocol::FieldValue + ).toUInt()); + protoText->setText( + proto->fieldData( + TextProtocol::textProto_text, + AbstractProtocol::FieldValue + ).toString()); +} + +void TextProtocolConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + TextProtocol::textProto_portNum, + portNumCombo->currentValue()); + proto->setFieldData( + TextProtocol::textProto_eol, + eolCombo->currentIndex()); + proto->setFieldData( + TextProtocol::textProto_encoding, + encodingCombo->currentIndex()); + + proto->setFieldData( + TextProtocol::textProto_text, + protoText->toPlainText()); +} + diff --git a/common/textprotoconfig.h b/common/textprotoconfig.h new file mode 100644 index 0000000..a1971ab --- /dev/null +++ b/common/textprotoconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_CONFIG_H +#define _TEXT_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_textproto.h" + +class TextProtocolConfigForm : + public AbstractProtocolConfigForm, + private Ui::TextProto +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); + virtual ~TextProtocolConfigForm(); + + static TextProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif From 88ec853d096d700e60f08f02d7b2d7fbd9f2930a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 22 Apr 2014 06:03:40 +0530 Subject: [PATCH 233/294] NOX: HexDumpProtocol - separated protocol and widget as per new framework --- common/hexdump.cpp | 52 ---------------------- common/hexdump.h | 38 +++++----------- common/hexdumpconfig.cpp | 75 ++++++++++++++++++++++++++++++++ common/hexdumpconfig.h | 44 +++++++++++++++++++ common/ostproto.pro | 8 ++-- common/ostprotogui.pro | 11 +++-- common/protocolmanager.cpp | 10 +++-- common/protocolwidgetfactory.cpp | 7 +++ 8 files changed, 154 insertions(+), 91 deletions(-) create mode 100644 common/hexdumpconfig.cpp create mode 100644 common/hexdumpconfig.h diff --git a/common/hexdump.cpp b/common/hexdump.cpp index f579430..376ccf0 100644 --- a/common/hexdump.cpp +++ b/common/hexdump.cpp @@ -20,35 +20,13 @@ along with this program. If not, see #include "hexdump.h" #include "streambase.h" -#include - -HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - - hexEdit->setFont(QFont("Courier")); - hexEdit->setOverwriteMode(false); -} - -void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) -{ - if (isOverwriteMode) - mode->setText(tr("Ovr")); - else - mode->setText(tr("Ins")); -} - HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - /* The configWidget is created lazily */ - configForm = NULL; } HexDumpProtocol::~HexDumpProtocol() { - delete configForm; } AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, @@ -231,33 +209,3 @@ int HexDumpProtocol::protocolFrameSize(int streamIndex) const return len; } -QWidget* HexDumpProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new HexDumpConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -void HexDumpProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->hexEdit->setData( - fieldData(hexDump_content, FieldValue).toByteArray()); - configForm->padUntilEnd->setChecked( - fieldData(hexDump_pad_until_end, FieldValue).toBool()); -} - -void HexDumpProtocol::storeConfigWidget() -{ - configWidget(); - - setFieldData(hexDump_content, configForm->hexEdit->data()); - setFieldData(hexDump_pad_until_end, configForm->padUntilEnd->isChecked()); -} - diff --git a/common/hexdump.h b/common/hexdump.h index f5b6932..4318192 100644 --- a/common/hexdump.h +++ b/common/hexdump.h @@ -20,10 +20,8 @@ along with this program. If not, see #ifndef _HEXDUMP_H #define _HEXDUMP_H -#include "hexdump.pb.h" -#include "ui_hexdump.h" - #include "abstractprotocol.h" +#include "hexdump.pb.h" /* HexDump Protocol Frame Format - @@ -33,18 +31,19 @@ HexDump Protocol Frame Format - +---------+---------+ */ -class HexDumpConfigForm : public QWidget, public Ui::HexDump -{ - Q_OBJECT -public: - HexDumpConfigForm(QWidget *parent = 0); -private slots: - void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); -}; - class HexDumpProtocol : public AbstractProtocol { public: + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~HexDumpProtocol(); @@ -68,22 +67,7 @@ public: virtual int protocolFrameSize(int streamIndex = 0) const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); - private: OstProto::HexDump data; - HexDumpConfigForm *configForm; - enum hexDumpfield - { - // Frame Fields - hexDump_content = 0, - - // Meta Fields - hexDump_pad_until_end, - - hexDump_fieldCount - }; }; #endif diff --git a/common/hexdumpconfig.cpp b/common/hexdumpconfig.cpp new file mode 100644 index 0000000..1c057b6 --- /dev/null +++ b/common/hexdumpconfig.cpp @@ -0,0 +1,75 @@ +/* +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 +*/ + +#include "hexdumpconfig.h" +#include "hexdump.h" + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +HexDumpConfigForm::~HexDumpConfigForm() +{ +} + +HexDumpConfigForm* HexDumpConfigForm::createInstance() +{ + return new HexDumpConfigForm; +} + +void HexDumpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hexEdit->setData( + proto->fieldData( + HexDumpProtocol::hexDump_content, + AbstractProtocol::FieldValue + ).toByteArray()); + padUntilEnd->setChecked( + proto->fieldData( + HexDumpProtocol::hexDump_pad_until_end, + AbstractProtocol::FieldValue + ).toBool()); +} + +void HexDumpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + HexDumpProtocol::hexDump_content, + hexEdit->data()); + proto->setFieldData( + HexDumpProtocol::hexDump_pad_until_end, + padUntilEnd->isChecked()); +} + +// +// ------------ private slots +// +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + diff --git a/common/hexdumpconfig.h b/common/hexdumpconfig.h new file mode 100644 index 0000000..b0dcfa7 --- /dev/null +++ b/common/hexdumpconfig.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _HEX_DUMP_CONFIG_H +#define _HEX_DUMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_hexdump.h" + +class HexDumpConfigForm : + public AbstractProtocolConfigForm, + private Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); + virtual ~HexDumpConfigForm(); + + static HexDumpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro index c69e4e0..7497d0c 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -69,11 +69,11 @@ HEADERS += \ mld.h \ tcp.h \ udp.h \ - textproto.h + textproto.h \ + hexdump.h HEADERS1 += \ userscript.h \ - hexdump.h \ sample.h SOURCES = \ @@ -102,11 +102,11 @@ SOURCES += \ mld.cpp \ tcp.cpp \ udp.cpp \ - textproto.cpp + textproto.cpp \ + hexdump.cpp SOURCES1 += \ userscript.cpp \ - hexdump.cpp \ sample.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 98596bc..3eab07e 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -1,6 +1,7 @@ TEMPLATE = lib CONFIG += qt staticlib QT += network xml +INCLUDEPATH += "../extra/qhexedit2/src" LIBS += \ -lprotobuf @@ -22,11 +23,11 @@ FORMS += \ icmp.ui \ tcp.ui \ udp.ui \ - textproto.ui + textproto.ui \ + hexdump.ui FORMS1 += \ userscript.ui \ - hexdump.ui \ sample.ui PROTOS = \ @@ -70,7 +71,8 @@ HEADERS += \ mldconfig.h \ tcpconfig.h \ udpconfig.h \ - textprotoconfig.h + textprotoconfig.h \ + hexdumpconfig.h SOURCES += \ ostprotolib.cpp \ @@ -100,7 +102,8 @@ SOURCES += \ mldconfig.cpp \ tcpconfig.cpp \ udpconfig.cpp \ - textprotoconfig.cpp + textprotoconfig.cpp \ + hexdumpconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index 8649dc9..beda573 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -23,7 +23,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 #include "userscript.h" -#include "hexdump.h" #include "sample.h" #else #include "mac.h" @@ -53,6 +52,8 @@ along with this program. If not, see #include "udp.h" // L5 Protos #include "textproto.h" +// Special Protos +#include "hexdump.h" #endif ProtocolManager *OstProtocolManager; @@ -63,9 +64,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, - (void*) HexDumpProtocol::createInstance); - registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, @@ -129,6 +127,10 @@ ProtocolManager::ProtocolManager() registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); + // Special Protocols + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + #endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 8c59eb4..927c565 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -46,6 +46,8 @@ along with this program. If not, see #include "udpconfig.h" // L5 Protocol Widgets #include "textprotoconfig.h" +// Special Protocol Widgets +#include "hexdumpconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -137,6 +139,11 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocolConfigForm::createInstance); + + // Special Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() From 7d3caad43cae9c6e7ce7c4b6e7abd7ac2440eeaa Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 25 Apr 2014 06:58:38 +0530 Subject: [PATCH 234/294] NOX: UserScript - separated protocol and widget as per new framework --- common/ostproto.pro | 8 +-- common/ostprotogui.pro | 12 ++-- common/protocolmanager.cpp | 6 +- common/protocolwidgetfactory.cpp | 4 ++ common/userscript.cpp | 79 +--------------------- common/userscript.h | 46 +++---------- common/userscriptconfig.cpp | 109 +++++++++++++++++++++++++++++++ common/userscriptconfig.h | 51 +++++++++++++++ 8 files changed, 189 insertions(+), 126 deletions(-) create mode 100644 common/userscriptconfig.cpp create mode 100644 common/userscriptconfig.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 7497d0c..d72b21b 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -70,10 +70,10 @@ HEADERS += \ tcp.h \ udp.h \ textproto.h \ - hexdump.h + hexdump.h \ + userscript.h HEADERS1 += \ - userscript.h \ sample.h SOURCES = \ @@ -103,10 +103,10 @@ SOURCES += \ tcp.cpp \ udp.cpp \ textproto.cpp \ - hexdump.cpp + hexdump.cpp \ + userscript.cpp SOURCES1 += \ - userscript.cpp \ sample.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 3eab07e..8be6d19 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -1,6 +1,6 @@ TEMPLATE = lib CONFIG += qt staticlib -QT += network xml +QT += network xml script INCLUDEPATH += "../extra/qhexedit2/src" LIBS += \ -lprotobuf @@ -24,10 +24,10 @@ FORMS += \ tcp.ui \ udp.ui \ textproto.ui \ - hexdump.ui + hexdump.ui \ + userscript.ui FORMS1 += \ - userscript.ui \ sample.ui PROTOS = \ @@ -72,7 +72,8 @@ HEADERS += \ tcpconfig.h \ udpconfig.h \ textprotoconfig.h \ - hexdumpconfig.h + hexdumpconfig.h \ + userscriptconfig.h SOURCES += \ ostprotolib.cpp \ @@ -103,7 +104,8 @@ SOURCES += \ tcpconfig.cpp \ udpconfig.cpp \ textprotoconfig.cpp \ - hexdumpconfig.cpp + hexdumpconfig.cpp \ + userscriptconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index beda573..c35dc1c 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -22,7 +22,6 @@ along with this program. If not, see #include "protocol.pb.h" #if 0 -#include "userscript.h" #include "sample.h" #else #include "mac.h" @@ -54,6 +53,7 @@ along with this program. If not, see #include "textproto.h" // Special Protos #include "hexdump.h" +#include "userscript.h" #endif ProtocolManager *OstProtocolManager; @@ -64,8 +64,6 @@ ProtocolManager::ProtocolManager() themselves (once this is done remove the #includes for all the protocols) */ #if 0 - registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, - (void*) UserScriptProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, (void*) SampleProtocol::createInstance); #else @@ -130,6 +128,8 @@ ProtocolManager::ProtocolManager() // Special Protocols registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); #endif populateNeighbourProtocols(); diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 927c565..78f47d1 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -48,6 +48,7 @@ along with this program. If not, see #include "textprotoconfig.h" // Special Protocol Widgets #include "hexdumpconfig.h" +#include "userscriptconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; @@ -144,6 +145,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() diff --git a/common/userscript.cpp b/common/userscript.cpp index fece963..f00ab6c 100644 --- a/common/userscript.cpp +++ b/common/userscript.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -19,54 +19,6 @@ along with this program. If not, see #include "userscript.h" -#include - -// -// -------------------- UserScriptConfigForm -------------------- -// - -UserScriptConfigForm::UserScriptConfigForm(UserScriptProtocol *protocol, - QWidget *parent) : QWidget(parent), protocol_(protocol) -{ - setupUi(this); - updateStatus(); -} - -void UserScriptConfigForm::updateStatus() -{ - if (protocol_->isScriptValid()) - { - statusLabel->setText(QString("Success")); - compileButton->setDisabled(true); - } - else - { - statusLabel->setText( - QString("Error: %1: %2").arg( - protocol_->userScriptErrorLineNumber()).arg( - protocol_->userScriptErrorText())); - compileButton->setEnabled(true); - } -} - -void UserScriptConfigForm::on_programEdit_textChanged() -{ - compileButton->setEnabled(true); -} - -void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) -{ - protocol_->storeConfigWidget(); - if (!protocol_->isScriptValid()) - { - QMessageBox::critical(this, "Error", - QString("%1: %2").arg( - protocol_->userScriptErrorLineNumber()).arg( - protocol_->userScriptErrorText())); - } - updateStatus(); -} - // // -------------------- UserScriptProtocol -------------------- // @@ -75,7 +27,6 @@ UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *par : AbstractProtocol(stream, parent), userProtocol_(this) { - configForm = NULL; isScriptValid_ = false; errorLineNumber_ = 0; @@ -88,7 +39,6 @@ UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *par UserScriptProtocol::~UserScriptProtocol() { - delete configForm; } AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, @@ -230,6 +180,7 @@ bool UserScriptProtocol::setFieldData(int index, const QVariant &value, case userScript_program: { data.set_program(value.toString().toStdString()); + evaluateUserScript(); break; } default: @@ -308,32 +259,6 @@ _do_default: return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } -QWidget* UserScriptProtocol::configWidget() -{ - if (configForm == NULL) - { - configForm = new UserScriptConfigForm(this); - loadConfigWidget(); - } - - return configForm; -} - -void UserScriptProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->programEdit->setPlainText( - fieldData(userScript_program, FieldValue).toString()); -} - -void UserScriptProtocol::storeConfigWidget() -{ - configWidget(); - setFieldData(userScript_program, configForm->programEdit->toPlainText()); - evaluateUserScript(); -} - void UserScriptProtocol::evaluateUserScript() const { QScriptValue userFunction; diff --git a/common/userscript.h b/common/userscript.h index 99ac226..f6bf6ef 100644 --- a/common/userscript.h +++ b/common/userscript.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -22,7 +22,6 @@ along with this program. If not, see #include "abstractprotocol.h" #include "userscript.pb.h" -#include "ui_userscript.h" #include #include @@ -99,30 +98,17 @@ private: int protocolFrameVariableCount_; }; - - -class UserScriptConfigForm : public QWidget, public Ui::UserScript -{ - Q_OBJECT - -public: - UserScriptConfigForm(UserScriptProtocol *protocol, QWidget *parent = 0); - -private: - void updateStatus(); - UserScriptProtocol *protocol_; - -private slots: - void on_programEdit_textChanged(); - void on_compileButton_clicked(bool checked = false); -}; - - - class UserScriptProtocol : public AbstractProtocol { - public: + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~UserScriptProtocol(); @@ -154,10 +140,6 @@ public: virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); - void evaluateUserScript() const; bool isScriptValid() const; int userScriptErrorLineNumber() const; @@ -166,15 +148,7 @@ public: private: int userScriptLineCount() const; - enum userScriptfield - { - // Frame Fields - userScript_program = 0, - - userScript_fieldCount - }; OstProto::UserScript data; - UserScriptConfigForm *configForm; mutable QScriptEngine engine_; mutable UserProtocol userProtocol_; @@ -186,5 +160,3 @@ private: }; #endif - -/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscriptconfig.cpp b/common/userscriptconfig.cpp new file mode 100644 index 0000000..85e0aba --- /dev/null +++ b/common/userscriptconfig.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2010, 2014 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 "userscriptconfig.h" +#include "userscript.h" + +#include + +UserScriptConfigForm::UserScriptConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + // The protocol_ (UserScriptProtocol) is a dummy protocol internal + // to UserScriptConfigForm whose sole purpose is to "compile" the script + // so that the configForm widget can display the compilation result. + // It is *not* used for actual packet contents at any time + protocol_ = new UserScriptProtocol(NULL); + compileScript(); +} + +UserScriptConfigForm::~UserScriptConfigForm() +{ + delete protocol_; +} + +UserScriptConfigForm* UserScriptConfigForm::createInstance() +{ + return new UserScriptConfigForm; +} + +void UserScriptConfigForm::loadWidget(AbstractProtocol *proto) +{ + programEdit->setPlainText( + proto->fieldData( + UserScriptProtocol::userScript_program, + AbstractProtocol::FieldValue + ).toString()); + + compileScript(); +} + +void UserScriptConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UserScriptProtocol::userScript_program, + programEdit->toPlainText()); +} + +// +// ----- private methods +// +void UserScriptConfigForm::compileScript() +{ + // storeWidget() will save the updated userscript into + // the protocol_ which in turn triggers the protocol_ to + // compile it + storeWidget(protocol_); + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +// +// ----- private slots +// +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + compileScript(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } +} + diff --git a/common/userscriptconfig.h b/common/userscriptconfig.h new file mode 100644 index 0000000..8285103 --- /dev/null +++ b/common/userscriptconfig.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _USER_SCRIPT_CONFIG_H +#define _USER_SCRIPT_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_userscript.h" + +class UserScriptProtocol; + +class UserScriptConfigForm : + public AbstractProtocolConfigForm, + private Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(QWidget *parent = 0); + virtual ~UserScriptConfigForm(); + + static UserScriptConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + void compileScript(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; +#endif From 6193db495d7adb9b754b22fd1db327e1d9fb7149 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 25 Apr 2014 07:52:55 +0530 Subject: [PATCH 235/294] NOX: SampleProtocol - separated protocol and widget as per new framework --- client/streamconfigdialog.cpp | 2 - common/ostproto.pro | 12 ++-- common/ostprotogui.pro | 14 ++-- common/protocolmanager.cpp | 29 ++++---- common/protocolwidgetfactory.cpp | 13 ++-- common/sample.cpp | 83 ++-------------------- common/sample.h | 24 ++----- common/sampleconfig.cpp | 117 +++++++++++++++++++++++++++++++ common/sampleconfig.h | 43 ++++++++++++ 9 files changed, 207 insertions(+), 130 deletions(-) create mode 100644 common/sampleconfig.cpp create mode 100644 common/sampleconfig.h diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index a9b94ea..8752f21 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -86,7 +86,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); -#if 0 // temp mask // Setup valid subsequent protocols for L2 to L4 protocols for (int i = ProtoL2; i <= ProtoL4; i++) { @@ -133,7 +132,6 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, } } } -#endif mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager->protocolDatabase(), this); diff --git a/common/ostproto.pro b/common/ostproto.pro index d72b21b..05024e4 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -46,7 +46,6 @@ HEADERS = \ HEADERS += \ mac.h \ - payload.h \ vlan.h \ svlan.h \ vlanstack.h \ @@ -71,11 +70,10 @@ HEADERS += \ udp.h \ textproto.h \ hexdump.h \ + payload.h \ + sample.h \ userscript.h -HEADERS1 += \ - sample.h - SOURCES = \ abstractprotocol.cpp \ crc32c.cpp \ @@ -86,7 +84,6 @@ SOURCES = \ SOURCES += \ mac.cpp \ - payload.cpp \ vlan.cpp \ svlan.cpp \ eth2.cpp \ @@ -104,11 +101,10 @@ SOURCES += \ udp.cpp \ textproto.cpp \ hexdump.cpp \ + payload.cpp \ + sample.cpp \ userscript.cpp -SOURCES1 += \ - sample.cpp - QMAKE_DISTCLEAN += object_script.* include(../protobuf.pri) diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 8be6d19..9fedf5d 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -10,7 +10,6 @@ FORMS = \ FORMS += \ mac.ui \ - payload.ui \ vlan.ui \ eth2.ui \ dot3.ui \ @@ -25,15 +24,14 @@ FORMS += \ udp.ui \ textproto.ui \ hexdump.ui \ + payload.ui \ + sample.ui \ userscript.ui -FORMS1 += \ - sample.ui - PROTOS = \ fileformat.proto -# TODO: Move fileformat related stuff into a different library +# TODO: Move fileformat related stuff into a different library - why? HEADERS = \ ostprotolib.h \ abstractfileformat.h \ @@ -51,7 +49,6 @@ HEADERS += \ comboprotocolconfig.h \ protocolwidgetfactory.h \ macconfig.h \ - payloadconfig.h \ vlanconfig.h \ svlanconfig.h \ vlanstackconfig.h \ @@ -73,6 +70,8 @@ HEADERS += \ udpconfig.h \ textprotoconfig.h \ hexdumpconfig.h \ + payloadconfig.h \ + sampleconfig.h \ userscriptconfig.h SOURCES += \ @@ -88,7 +87,6 @@ SOURCES += \ SOURCES += \ protocolwidgetfactory.cpp \ macconfig.cpp \ - payloadconfig.cpp \ vlanconfig.cpp \ eth2config.cpp \ dot3config.cpp \ @@ -105,6 +103,8 @@ SOURCES += \ udpconfig.cpp \ textprotoconfig.cpp \ hexdumpconfig.cpp \ + payloadconfig.cpp \ + sampleconfig.cpp \ userscriptconfig.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp index c35dc1c..84e1e2d 100644 --- a/common/protocolmanager.cpp +++ b/common/protocolmanager.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -21,20 +21,20 @@ along with this program. If not, see #include "abstractprotocol.h" #include "protocol.pb.h" -#if 0 -#include "sample.h" -#else + #include "mac.h" -#include "payload.h" #include "vlan.h" #include "svlan.h" -#include "vlanstack.h" +#include "vlanstack.h" + +// L2 Protos #include "dot3.h" #include "llc.h" #include "dot2llc.h" #include "snap.h" #include "dot2snap.h" #include "eth2.h" + // L3 Protos #include "arp.h" #include "ip4.h" @@ -43,18 +43,22 @@ along with this program. If not, see #include "ip4over6.h" #include "ip6over4.h" #include "ip6over6.h" + // L4 Protos #include "icmp.h" #include "igmp.h" #include "mld.h" #include "tcp.h" #include "udp.h" + // L5 Protos #include "textproto.h" + // Special Protos #include "hexdump.h" +#include "payload.h" +#include "sample.h" #include "userscript.h" -#endif ProtocolManager *OstProtocolManager; @@ -63,14 +67,8 @@ ProtocolManager::ProtocolManager() /*! \todo (LOW) calls to registerProtocol() should be done by the protocols themselves (once this is done remove the #includes for all the protocols) */ -#if 0 - registerProtocol(OstProto::Protocol::kSampleFieldNumber, - (void*) SampleProtocol::createInstance); -#else registerProtocol(OstProto::Protocol::kMacFieldNumber, (void*) MacProtocol::createInstance); - registerProtocol(OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, (void*) VlanProtocol::createInstance); @@ -128,10 +126,13 @@ ProtocolManager::ProtocolManager() // Special Protocols registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptProtocol::createInstance); -#endif populateNeighbourProtocols(); } diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp index 78f47d1..8c8e30a 100644 --- a/common/protocolwidgetfactory.cpp +++ b/common/protocolwidgetfactory.cpp @@ -20,10 +20,10 @@ along with this program. If not, see #include "protocolwidgetfactory.h" #include "macconfig.h" -#include "payloadconfig.h" #include "vlanconfig.h" #include "svlanconfig.h" #include "vlanstackconfig.h" +// L2 Protocol Widgets #include "eth2config.h" #include "dot3config.h" #include "llcconfig.h" @@ -48,6 +48,8 @@ along with this program. If not, see #include "textprotoconfig.h" // Special Protocol Widgets #include "hexdumpconfig.h" +#include "payloadconfig.h" +#include "sampleconfig.h" #include "userscriptconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; @@ -62,9 +64,6 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kMacFieldNumber, (void*) MacConfigForm::createInstance); - OstProtocolWidgetFactory->registerProtocolConfigWidget( - OstProto::Protocol::kPayloadFieldNumber, - (void*) PayloadConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kVlanFieldNumber, @@ -145,6 +144,12 @@ ProtocolWidgetFactory::ProtocolWidgetFactory() OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSampleFieldNumber, + (void*) SampleConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptConfigForm::createInstance); diff --git a/common/sample.cpp b/common/sample.cpp index 42f9f32..0432cf2 100644 --- a/common/sample.cpp +++ b/common/sample.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -17,26 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include - #include "sample.h" -SampleConfigForm::SampleConfigForm(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); -} - SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { - /* The configWidget is created lazily */ - configForm = NULL; } SampleProtocol::~SampleProtocol() { - delete configForm; } AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, @@ -97,7 +86,9 @@ quint32 SampleProtocol::protocolId(ProtocolIdType type) const { switch(type) { - case ProtocolIdIp: return 1234; + case ProtocolIdLlc: return 0xFFFFFF; + case ProtocolIdEth: return 0xFFFF; + case ProtocolIdIp: return 0xFF; default:break; } @@ -120,7 +111,7 @@ int SampleProtocol::fieldCount() const */ int SampleProtocol::frameFieldCount() const { - return 0; + return AbstractProtocol::frameFieldCount(); } /*! @@ -384,14 +375,14 @@ bool SampleProtocol::setFieldData(int index, const QVariant &value, { uint a = value.toUInt(&isOk); if (isOk) - data.set_ab((data.ab() & 0xe000) | (a << 13)); + data.set_ab((data.ab() & 0x1FFF) | ((a & 0x07) << 13)); break; } case sample_b: { uint b = value.toUInt(&isOk); if (isOk) - data.set_ab((data.ab() & 0x1FFF) | b); + data.set_ab((data.ab() & 0xe000) | (b & 0x1FFF)); break; } case sample_payloadLength: @@ -484,63 +475,3 @@ int SampleProtocol::protocolFrameVariableCount() const { return 1; } - -QWidget* SampleProtocol::configWidget() -{ - /* Lazy creation of the configWidget */ - if (configForm == NULL) - { - configForm = new SampleConfigForm; - loadConfigWidget(); - } - - return configForm; -} - -/*! -TODO: Edit this function to load each field's data into the config Widget - -See AbstractProtocol::loadConfigWidget() for more info -*/ -void SampleProtocol::loadConfigWidget() -{ - configWidget(); - - configForm->sampleA->setText(fieldData(sample_a, FieldValue).toString()); - configForm->sampleB->setText(fieldData(sample_b, FieldValue).toString()); - - configForm->samplePayloadLength->setText( - fieldData(sample_payloadLength, FieldValue).toString()); - - configForm->isChecksumOverride->setChecked( - fieldData(sample_is_override_checksum, FieldValue).toBool()); - configForm->sampleChecksum->setText(uintToHexStr( - fieldData(sample_checksum, FieldValue).toUInt(), 2)); - - configForm->sampleX->setText(fieldData(sample_x, FieldValue).toString()); - configForm->sampleY->setText(fieldData(sample_y, FieldValue).toString()); - -} - -/*! -TODO: Edit this function to store each field's data from the config Widget - -See AbstractProtocol::storeConfigWidget() for more info -*/ -void SampleProtocol::storeConfigWidget() -{ - bool isOk; - - configWidget(); - setFieldData(sample_a, configForm->sampleA->text()); - setFieldData(sample_b, configForm->sampleB->text()); - - setFieldData(sample_payloadLength, configForm->samplePayloadLength->text()); - setFieldData(sample_is_override_checksum, - configForm->isChecksumOverride->isChecked()); - setFieldData(sample_checksum, configForm->sampleChecksum->text().toUInt(&isOk, BASE_HEX)); - - setFieldData(sample_x, configForm->sampleX->text()); - setFieldData(sample_y, configForm->sampleY->text()); -} - diff --git a/common/sample.h b/common/sample.h index 90b9be8..475e72f 100644 --- a/common/sample.h +++ b/common/sample.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -20,10 +20,8 @@ along with this program. If not, see #ifndef _SAMPLE_H #define _SAMPLE_H -#include "sample.pb.h" -#include "ui_sample.h" - #include "abstractprotocol.h" +#include "sample.pb.h" /* Sample Protocol Frame Format - @@ -34,19 +32,9 @@ Sample Protocol Frame Format - Figures in brackets represent field width in bits */ -class SampleConfigForm : public QWidget, public Ui::Sample -{ - Q_OBJECT -public: - SampleConfigForm(QWidget *parent = 0); -private slots: -}; - class SampleProtocol : public AbstractProtocol { -private: - OstProto::Sample data; - SampleConfigForm *configForm; +public: enum samplefield { // Frame Fields @@ -63,7 +51,6 @@ private: sample_fieldCount }; -public: SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SampleProtocol(); @@ -95,9 +82,8 @@ public: virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; - virtual QWidget* configWidget(); - virtual void loadConfigWidget(); - virtual void storeConfigWidget(); +private: + OstProto::Sample data; }; #endif diff --git a/common/sampleconfig.cpp b/common/sampleconfig.cpp new file mode 100644 index 0000000..caaf0d2 --- /dev/null +++ b/common/sampleconfig.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2010, 2014 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 "sampleconfig.h" +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SampleConfigForm::~SampleConfigForm() +{ +} + +SampleConfigForm* SampleConfigForm::createInstance() +{ + return new SampleConfigForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocolConfigForm::loadWidget() for more info +*/ +void SampleConfigForm::loadWidget(AbstractProtocol *proto) +{ + sampleA->setText( + proto->fieldData( + SampleProtocol::sample_a, + AbstractProtocol::FieldValue + ).toString()); + sampleB->setText( + proto->fieldData( + SampleProtocol::sample_b, + AbstractProtocol::FieldValue + ).toString()); + + samplePayloadLength->setText( + proto->fieldData( + SampleProtocol::sample_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isChecksumOverride->setChecked( + proto->fieldData( + SampleProtocol::sample_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + sampleChecksum->setText(uintToHexStr( + proto->fieldData( + SampleProtocol::sample_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + sampleX->setText( + proto->fieldData( + SampleProtocol::sample_x, + AbstractProtocol::FieldValue + ).toString()); + sampleY->setText( + proto->fieldData( + SampleProtocol::sample_y, + AbstractProtocol::FieldValue + ).toString()); +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocolConfigForm::storeWidget() for more info +*/ +void SampleConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SampleProtocol::sample_a, + sampleA->text()); + proto->setFieldData( + SampleProtocol::sample_b, + sampleB->text()); + + proto->setFieldData( + SampleProtocol::sample_payloadLength, + samplePayloadLength->text()); + proto->setFieldData( + SampleProtocol::sample_is_override_checksum, + + isChecksumOverride->isChecked()); + proto->setFieldData( + SampleProtocol::sample_checksum, + hexStrToUInt(sampleChecksum->text())); + + proto->setFieldData( + SampleProtocol::sample_x, + sampleX->text()); + proto->setFieldData( + SampleProtocol::sample_y, + sampleY->text()); +} + diff --git a/common/sampleconfig.h b/common/sampleconfig.h new file mode 100644 index 0000000..8e85976 --- /dev/null +++ b/common/sampleconfig.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _SAMPLE_CONFIG_H +#define _SAMPLE_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_sample.h" + +class SampleConfigForm : + public AbstractProtocolConfigForm, + private Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); + virtual ~SampleConfigForm(); + + static SampleConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: +}; + +#endif From 40837fae407ef6c6d3ab681fd150ca4749ca92ad Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 1 May 2014 21:15:41 +0530 Subject: [PATCH 236/294] NOX - Extracted each xxxPdmlProtocol from pdmlprotocols.h/.cpp into its own .h/.cpp. It is now more evident that while implementing a new protocol, one has to implement the xxxConfigFrom and xxxPdmlProtocol as well. --- common/arppdml.cpp | 41 ++ common/arppdml.h | 34 ++ common/eth2pdml.cpp | 124 ++++ common/eth2pdml.h | 38 ++ common/icmp6pdml.cpp | 100 ++++ common/icmp6pdml.h | 50 ++ common/icmppdml.cpp | 93 +++ common/icmppdml.h | 48 ++ common/igmppdml.cpp | 141 +++++ common/igmppdml.h | 49 ++ common/ip4pdml.cpp | 93 +++ common/ip4pdml.h | 41 ++ common/ip6pdml.cpp | 76 +++ common/ip6pdml.h | 39 ++ common/llcpdml.cpp | 80 +++ common/llcpdml.h | 39 ++ common/mldpdml.cpp | 133 +++++ common/mldpdml.h | 47 ++ common/ostprotogui.pro | 16 + common/pcapfileformat.cpp | 2 +- common/pdmlprotocol.cpp | 2 - common/pdmlprotocol.h | 2 + common/pdmlprotocols.cpp | 1162 ------------------------------------- common/pdmlprotocols.h | 242 -------- common/pdmlreader.cpp | 15 + common/svlanpdml.cpp | 110 ++++ common/svlanpdml.h | 40 ++ common/tcppdml.cpp | 98 ++++ common/tcppdml.h | 42 ++ common/textprotopdml.cpp | 171 ++++++ common/textprotopdml.h | 53 ++ common/udppdml.cpp | 53 ++ common/udppdml.h | 35 ++ common/vlanpdml.cpp | 91 +++ common/vlanpdml.h | 40 ++ test/test.pro | 13 +- 36 files changed, 2042 insertions(+), 1411 deletions(-) create mode 100644 common/arppdml.cpp create mode 100644 common/arppdml.h create mode 100644 common/eth2pdml.cpp create mode 100644 common/eth2pdml.h create mode 100644 common/icmp6pdml.cpp create mode 100644 common/icmp6pdml.h create mode 100644 common/icmppdml.cpp create mode 100644 common/icmppdml.h create mode 100644 common/igmppdml.cpp create mode 100644 common/igmppdml.h create mode 100644 common/ip4pdml.cpp create mode 100644 common/ip4pdml.h create mode 100644 common/ip6pdml.cpp create mode 100644 common/ip6pdml.h create mode 100644 common/llcpdml.cpp create mode 100644 common/llcpdml.h create mode 100644 common/mldpdml.cpp create mode 100644 common/mldpdml.h create mode 100644 common/svlanpdml.cpp create mode 100644 common/svlanpdml.h create mode 100644 common/tcppdml.cpp create mode 100644 common/tcppdml.h create mode 100644 common/textprotopdml.cpp create mode 100644 common/textprotopdml.h create mode 100644 common/udppdml.cpp create mode 100644 common/udppdml.h create mode 100644 common/vlanpdml.cpp create mode 100644 common/vlanpdml.h diff --git a/common/arppdml.cpp b/common/arppdml.cpp new file mode 100644 index 0000000..9580db7 --- /dev/null +++ b/common/arppdml.cpp @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 "arppdml.h" + +#include "arp.pb.h" + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + diff --git a/common/arppdml.h b/common/arppdml.h new file mode 100644 index 0000000..039ed9b --- /dev/null +++ b/common/arppdml.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ARP_PDML_H +#define _ARP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +#endif diff --git a/common/eth2pdml.cpp b/common/eth2pdml.cpp new file mode 100644 index 0000000..8bb8d17 --- /dev/null +++ b/common/eth2pdml.cpp @@ -0,0 +1,124 @@ +/* +Copyright (C) 2011 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 "eth2pdml.h" + +#include "dot3.pb.h" +#include "eth2.pb.h" +#include "mac.pb.h" +#include "vlan.pb.h" + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + diff --git a/common/eth2pdml.h b/common/eth2pdml.h new file mode 100644 index 0000000..b776147 --- /dev/null +++ b/common/eth2pdml.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ETH2_PDML_H +#define _ETH2_PDML_H + +#include "pdmlprotocol.h" + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +#endif diff --git a/common/icmp6pdml.cpp b/common/icmp6pdml.cpp new file mode 100644 index 0000000..53c4a34 --- /dev/null +++ b/common/icmp6pdml.cpp @@ -0,0 +1,100 @@ +/* +Copyright (C) 2011 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 "icmp6pdml.h" + +#include "icmp.pb.h" +#include "sample.pb.h" + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + diff --git a/common/icmp6pdml.h b/common/icmp6pdml.h new file mode 100644 index 0000000..d159824 --- /dev/null +++ b/common/icmp6pdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP6_PDML_H +#define _ICMP6_PDML_H + +#include "pdmlprotocol.h" + +#include "icmppdml.h" +#include "mldpdml.h" + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +#endif diff --git a/common/icmppdml.cpp b/common/icmppdml.cpp new file mode 100644 index 0000000..0bff798 --- /dev/null +++ b/common/icmppdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "icmppdml.h" + +#include "icmp.pb.h" + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/icmppdml.h b/common/icmppdml.h new file mode 100644 index 0000000..58b3e37 --- /dev/null +++ b/common/icmppdml.h @@ -0,0 +1,48 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP_PDML_H +#define _ICMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +#endif diff --git a/common/igmppdml.cpp b/common/igmppdml.cpp new file mode 100644 index 0000000..19516d7 --- /dev/null +++ b/common/igmppdml.cpp @@ -0,0 +1,141 @@ +/* +Copyright (C) 2011 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 "igmppdml.h" + +#include "igmp.pb.h" + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/igmppdml.h b/common/igmppdml.h new file mode 100644 index 0000000..4b553a7 --- /dev/null +++ b/common/igmppdml.h @@ -0,0 +1,49 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IGMP_PDML_H +#define _IGMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +#endif diff --git a/common/ip4pdml.cpp b/common/ip4pdml.cpp new file mode 100644 index 0000000..f355260 --- /dev/null +++ b/common/ip4pdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "ip4pdml.h" + +#include "hexdump.pb.h" +#include "ip4.pb.h" + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/ip4pdml.h b/common/ip4pdml.h new file mode 100644 index 0000000..64f818d --- /dev/null +++ b/common/ip4pdml.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP4_PDML_H +#define _IP4_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +#endif diff --git a/common/ip6pdml.cpp b/common/ip6pdml.cpp new file mode 100644 index 0000000..2f3a7f8 --- /dev/null +++ b/common/ip6pdml.cpp @@ -0,0 +1,76 @@ +/* +Copyright (C) 2011 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 "ip6pdml.h" + +#include "ip6.pb.h" + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + diff --git a/common/ip6pdml.h b/common/ip6pdml.h new file mode 100644 index 0000000..4f766b4 --- /dev/null +++ b/common/ip6pdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP6_PDML_H +#define _IP6_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +#endif diff --git a/common/llcpdml.cpp b/common/llcpdml.cpp new file mode 100644 index 0000000..a5584cc --- /dev/null +++ b/common/llcpdml.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2011 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 "llcpdml.h" + +#include "llc.pb.h" +#include "snap.pb.h" + +#include + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + diff --git a/common/llcpdml.h b/common/llcpdml.h new file mode 100644 index 0000000..ace0dcb --- /dev/null +++ b/common/llcpdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _LLC_PDML_H +#define _LLC_PDML_H + +#include "pdmlprotocol.h" + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +#endif diff --git a/common/mldpdml.cpp b/common/mldpdml.cpp new file mode 100644 index 0000000..b17503e --- /dev/null +++ b/common/mldpdml.cpp @@ -0,0 +1,133 @@ +/* +Copyright (C) 2011 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 "mldpdml.h" + +#include "mld.pb.h" + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + diff --git a/common/mldpdml.h b/common/mldpdml.h new file mode 100644 index 0000000..0c47fbe --- /dev/null +++ b/common/mldpdml.h @@ -0,0 +1,47 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _MLD_PDML_H +#define _MLD_PDML_H + +#include "pdmlprotocol.h" + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +#endif diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 9fedf5d..0bf1c3f 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -107,6 +107,22 @@ SOURCES += \ sampleconfig.cpp \ userscriptconfig.cpp +SOURCES += \ + vlanpdml.cpp \ + svlanpdml.cpp \ + eth2pdml.cpp \ + llcpdml.cpp \ + arppdml.cpp \ + ip4pdml.cpp \ + ip6pdml.cpp \ + icmppdml.cpp \ + icmp6pdml.cpp \ + igmppdml.cpp \ + mldpdml.cpp \ + tcppdml.cpp \ + udppdml.cpp \ + textprotopdml.cpp + QMAKE_DISTCLEAN += object_script.* include(../protobuf.pri) diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp index bc0ccd6..e96b684 100644 --- a/common/pcapfileformat.cpp +++ b/common/pcapfileformat.cpp @@ -615,7 +615,7 @@ bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); if (s.packetRate()) - pktHdr.tsUsec += 1000000/s.packetRate(); + pktHdr.tsUsec += quint32(1e6/s.packetRate()); if (pktHdr.tsUsec >= 1000000) { pktHdr.tsSec++; diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp index cb39a4f..2be78ab 100644 --- a/common/pdmlprotocol.cpp +++ b/common/pdmlprotocol.cpp @@ -19,8 +19,6 @@ along with this program. If not, see #include "pdmlprotocol.h" -const int kBaseHex = 16; - PdmlProtocol::PdmlProtocol() { ostProtoId_ = -1; diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h index 412f588..7f23524 100644 --- a/common/pdmlprotocol.h +++ b/common/pdmlprotocol.h @@ -27,6 +27,8 @@ along with this program. If not, see #include #include +const int kBaseHex = 16; + class PdmlProtocol { public: diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp index 31e1303..c64d46d 100644 --- a/common/pdmlprotocols.cpp +++ b/common/pdmlprotocols.cpp @@ -19,30 +19,7 @@ along with this program. If not, see #include "pdmlprotocols.h" -#include "arp.pb.h" -#include "eth2.pb.h" -#include "dot3.pb.h" -#include "gmp.pb.h" #include "hexdump.pb.h" -#include "llc.pb.h" -#include "mac.pb.h" -#include "icmp.pb.h" -#include "igmp.pb.h" -#include "ip4.pb.h" -#include "ip6.pb.h" -#include "mld.pb.h" -#include "sample.pb.h" -#include "snap.pb.h" -#include "svlan.pb.h" -#include "tcp.pb.h" -#include "textproto.pb.h" -#include "udp.pb.h" -#include "vlan.pb.h" - -#include -#include - -const int kBaseHex = 16; // ---------------------------------------------------------- // // PdmlUnknownProtocol // @@ -216,1142 +193,3 @@ void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, } } } - - -// ---------------------------------------------------------- // -// PdmlSvlanProtocol // -// ---------------------------------------------------------- // - -PdmlSvlanProtocol::PdmlSvlanProtocol() -{ - ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; -} - -PdmlProtocol* PdmlSvlanProtocol::createInstance() -{ - return new PdmlSvlanProtocol(); -} - -void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); - - svlan->set_tpid(0x88a8); - svlan->set_is_override_tpid(true); - - // If a eth2 protocol precedes svlan, we remove the eth2 protocol - // 'coz the eth2.etherType is actually the svlan.tpid - // - // We assume that the current protocol is the last in the stream - int index = stream->protocol_size() - 1; - if ((index > 1) - && (stream->protocol(index).protocol_id().id() - == OstProto::Protocol::kSvlanFieldNumber) - && (stream->protocol(index - 1).protocol_id().id() - == OstProto::Protocol::kEth2FieldNumber)) - { - stream->mutable_protocol()->SwapElements(index, index - 1); - Q_ASSERT(stream->protocol(index).protocol_id().id() - == OstProto::Protocol::kEth2FieldNumber); - stream->mutable_protocol()->RemoveLast(); - } -} - -void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) - { - bool isOk; - OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); - uint tag = attributes.value("unmaskedvalue").isEmpty() ? - attributes.value("value").toString().toUInt(&isOk, kBaseHex) : - attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); - - svlan->set_vlan_tag(tag); - } - else if (name == "ieee8021ad.cvid") - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kSvlanFieldNumber); - - OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); - - svlan->set_tpid(0x88a8); - svlan->set_is_override_tpid(true); - - bool isOk; - uint tag = attributes.value("unmaskedvalue").isEmpty() ? - attributes.value("value").toString().toUInt(&isOk, kBaseHex) : - attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); - - svlan->set_vlan_tag(tag); - } - else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kEth2FieldNumber); - - bool isOk; - OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); - - eth2->set_type(attributes.value("value") - .toString().toUInt(&isOk, kBaseHex)); - eth2->set_is_override_type(true); - } -} - - -// ---------------------------------------------------------- // -// PdmlVlanProtocol // -// ---------------------------------------------------------- // - -PdmlVlanProtocol::PdmlVlanProtocol() -{ - ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; -} - -PdmlProtocol* PdmlVlanProtocol::createInstance() -{ - return new PdmlVlanProtocol(); -} - -void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); - - vlan->set_tpid(0x8100); - vlan->set_is_override_tpid(true); - - // If a eth2 protocol precedes vlan, we remove the eth2 protocol - // 'coz the eth2.etherType is actually the vlan.tpid - // - // We assume that the current protocol is the last in the stream - int index = stream->protocol_size() - 1; - if ((index > 1) - && (stream->protocol(index).protocol_id().id() - == OstProto::Protocol::kVlanFieldNumber) - && (stream->protocol(index - 1).protocol_id().id() - == OstProto::Protocol::kEth2FieldNumber)) - { - stream->mutable_protocol()->SwapElements(index, index - 1); - Q_ASSERT(stream->protocol(index).protocol_id().id() - == OstProto::Protocol::kEth2FieldNumber); - stream->mutable_protocol()->RemoveLast(); - } -} - -void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - if (name == "vlan.id") - { - bool isOk; - OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); - uint tag = attributes.value("unmaskedvalue").isEmpty() ? - attributes.value("value").toString().toUInt(&isOk, kBaseHex) : - attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); - - vlan->set_vlan_tag(tag); - } - else if (name == "vlan.etype") - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kEth2FieldNumber); - - bool isOk; - OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); - - eth2->set_type(attributes.value("value") - .toString().toUInt(&isOk, kBaseHex)); - eth2->set_is_override_type(true); - } -} - - -// ---------------------------------------------------------- // -// PdmlEthProtocol // -// ---------------------------------------------------------- // - -PdmlEthProtocol::PdmlEthProtocol() -{ - ostProtoId_ = OstProto::Protocol::kMacFieldNumber; - - fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); - fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); -} - -PdmlProtocol* PdmlEthProtocol::createInstance() -{ - return new PdmlEthProtocol(); -} - -void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) -{ - if (name == "eth.vlan.tpid") - { - bool isOk; - - uint tpid = attributes.value("value").toString() - .toUInt(&isOk, kBaseHex); - - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kVlanFieldNumber); - - OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); - - vlan->set_tpid(tpid); - vlan->set_is_override_tpid(true); - } - else if (name == "eth.vlan.id") - { - bool isOk; - - uint tag = attributes.value("unmaskedvalue").isEmpty() ? - attributes.value("value").toString().toUInt(&isOk, kBaseHex) : - attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); - - OstProto::Vlan *vlan = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::vlan); - - vlan->set_vlan_tag(tag); - } - else if (name == "eth.type") - { - bool isOk; - - uint type = attributes.value("value").toString() - .toUInt(&isOk, kBaseHex); - - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kEth2FieldNumber); - - OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); - - eth2->set_type(type); - eth2->set_is_override_type(true); - } - else if (name == "eth.len") - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kDot3FieldNumber); - - OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); - - bool isOk; - dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); - dot3->set_is_override_length(true); - } -#if 0 - else if (name == "eth.trailer") - { - QByteArray trailer = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - - stream->mutable_core()->mutable_name()->append(trailer.constData(), - trailer.size()); - } - else if ((name == "eth.fcs") || - attributes.value("show").toString().startsWith("Frame check sequence")) - { - QByteArray trailer = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - - stream->mutable_core()->mutable_name()->append(trailer.constData(), - trailer.size()); - } -#endif -} - - -// ---------------------------------------------------------- // -// PdmlLlcProtocol // -// ---------------------------------------------------------- // - -PdmlLlcProtocol::PdmlLlcProtocol() -{ - ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; - - fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); - fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); - fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); -} - -PdmlProtocol* PdmlLlcProtocol::createInstance() -{ - return new PdmlLlcProtocol(); -} - -void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) -{ - if (name == "llc.oui") - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kSnapFieldNumber); - - OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); - - bool isOk; - snap->set_oui(attributes.value("value").toString() - .toUInt(&isOk, kBaseHex)); - snap->set_is_override_oui(true); - } - else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) - { - OstProto::Snap *snap = stream->mutable_protocol( - stream->protocol_size()-1)->MutableExtension(OstProto::snap); - - bool isOk; - snap->set_type(attributes.value("value").toString() - .toUInt(&isOk, kBaseHex)); - snap->set_is_override_type(true); - } -} - -void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream* /*stream*/) -{ - OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); - - llc->set_is_override_dsap(true); - llc->set_is_override_ssap(true); - llc->set_is_override_ctl(true); -} - - -// ---------------------------------------------------------- // -// PdmlArpProtocol // -// ---------------------------------------------------------- // - -PdmlArpProtocol::PdmlArpProtocol() -{ - ostProtoId_ = OstProto::Protocol::kArpFieldNumber; - - fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); - fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); - fieldMap_.insert("arp.src.proto_ipv4", - OstProto::Arp::kSenderProtoAddrFieldNumber); - fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); - fieldMap_.insert("arp.dst.proto_ipv4", - OstProto::Arp::kTargetProtoAddrFieldNumber); -} - -PdmlProtocol* PdmlArpProtocol::createInstance() -{ - return new PdmlArpProtocol(); -} - - -// ---------------------------------------------------------- // -// PdmlIp4Protocol // -// ---------------------------------------------------------- // - -PdmlIp4Protocol::PdmlIp4Protocol() -{ - ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; - - fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); - fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); - fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); - fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); - //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); - fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); - fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); - fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); - fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); - fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); - fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); -} - -PdmlProtocol* PdmlIp4Protocol::createInstance() -{ - return new PdmlIp4Protocol(); -} - -void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - - if ((name == "ip.options") || - attributes.value("show").toString().startsWith("Options")) - { - options_ = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - } - else if (name == "ip.flags") - { - OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); - - ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); - } -} - -void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); - - ip4->set_is_override_ver(true); - ip4->set_is_override_hdrlen(true); - ip4->set_is_override_totlen(true); - ip4->set_is_override_proto(true); - ip4->set_is_override_cksum(true); - - if (options_.size()) - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - hexDump->mutable_content()->append(options_.constData(), - options_.size()); - hexDump->set_pad_until_end(false); - options_.resize(0); - } -} - -// ---------------------------------------------------------- // -// PdmlIp6Protocol // -// ---------------------------------------------------------- // - -PdmlIp6Protocol::PdmlIp6Protocol() -{ - ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; - - fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); - fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); - fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); - fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); - fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); - fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); - - // ipv6.src and ipv6.dst handled as unknown fields -} - -PdmlProtocol* PdmlIp6Protocol::createInstance() -{ - return new PdmlIp6Protocol(); -} - -void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - - if (name == "ipv6.src") - { - OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); - QString addrHexStr = attributes.value("value").toString(); - - ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); - ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } - else if (name == "ipv6.dst") - { - OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); - QString addrHexStr = attributes.value("value").toString(); - - ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); - ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } -} - -void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream* /*stream*/) -{ - OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); - - ip6->set_is_override_version(true); - ip6->set_is_override_payload_length(true); - ip6->set_is_override_next_header(true); -} - - -// ---------------------------------------------------------- // -// PdmlIcmpProtocol // -// ---------------------------------------------------------- // - -PdmlIcmpProtocol::PdmlIcmpProtocol() -{ - ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; - - fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); - fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); - fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); - fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); - fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); - - fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); - fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); - fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); - fieldMap_.insert("icmpv6.echo.identifier", - OstProto::Icmp::kIdentifierFieldNumber); - fieldMap_.insert("icmpv6.echo.sequence_number", - OstProto::Icmp::kSequenceFieldNumber); -} - -PdmlProtocol* PdmlIcmpProtocol::createInstance() -{ - return new PdmlIcmpProtocol(); -} - -void PdmlIcmpProtocol::preProtocolHandler(QString name, - const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); - - if (name == "icmp") - icmp->set_icmp_version(OstProto::Icmp::kIcmp4); - else if (name == "icmpv6") - icmp->set_icmp_version(OstProto::Icmp::kIcmp6); - - icmp->set_is_override_checksum(true); - - icmp->set_type(kIcmpInvalidType); -} - -void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); - - if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) - && (icmp->type() >= kIcmp6EchoRequest) - && (icmp->type() <= kIcmp6EchoReply)) - { - QString addrHexStr = attributes.value("value").toString(); - - // Wireshark 1.4.x does not have these as filterable fields - if (attributes.value("show").toString().startsWith("ID")) - icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); - else if (attributes.value("show").toString().startsWith("Sequence")) - icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); - } -} - -void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); - - if (icmp->type() == kIcmpInvalidType) - stream->mutable_protocol()->RemoveLast(); -} - -// ---------------------------------------------------------- // -// PdmlIcmp6Protocol // -// ---------------------------------------------------------- // - -PdmlIcmp6Protocol::PdmlIcmp6Protocol() -{ - ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; - - proto_ = NULL; -} - -PdmlProtocol* PdmlIcmp6Protocol::createInstance() -{ - return new PdmlIcmp6Protocol(); -} - -void PdmlIcmp6Protocol::preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, - int expectedPos, OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - proto_ = NULL; - ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; - icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); - mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); -} - -void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - if (proto_) - proto_->postProtocolHandler(pbProto, stream); - else - stream->mutable_protocol()->RemoveLast(); - - proto_ = NULL; - ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; -} - -void PdmlIcmp6Protocol::unknownFieldHandler(QString name, - int pos, int size, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - if (proto_) - { - proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, - stream); - } - else if (name == "icmpv6.type") - { - bool isOk; - uint type = attributes.value("value").toString().toUInt( - &isOk, kBaseHex); - - if (((type >= 130) && (type <= 132)) || (type == 143)) - { - // MLD - proto_ = &mld_; - fieldMap_ = mld_.fieldMap_; - ostProtoId_ = OstProto::Protocol::kMldFieldNumber; - } - else - { - // ICMP - proto_ = &icmp_; - fieldMap_ = icmp_.fieldMap_; - ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; - } - - pbProto->mutable_protocol_id()->set_id(ostProtoId_); - pbProto->MutableExtension(OstProto::sample)->Clear(); - - fieldHandler(name, attributes, pbProto, stream); - } - else - { - qDebug("unexpected field %s", name.toAscii().constData()); - } -} - - -// ---------------------------------------------------------- // -// PdmlIgmpProtocol // -// ---------------------------------------------------------- // - -PdmlIgmpProtocol::PdmlIgmpProtocol() -{ - ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; - - fieldMap_.insert("igmp.max_resp", - OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME - fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); - - fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); - fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); - fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME - - fieldMap_.insert("igmp.num_grp_recs", - OstProto::Gmp::kGroupRecordCountFieldNumber); -} - -PdmlProtocol* PdmlIgmpProtocol::createInstance() -{ - return new PdmlIgmpProtocol(); -} - -void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); - - igmp->set_is_override_rsvd_code(true); - igmp->set_is_override_checksum(true); - igmp->set_is_override_source_count(true); - igmp->set_is_override_group_record_count(true); - - version_ = 0; -} - -void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); - QString valueHexStr = attributes.value("value").toString(); - - if (name == "igmp.version") - { - version_ = attributes.value("show").toString().toUInt(&isOk); - } - else if (name == "igmp.type") - { - uint type = valueHexStr.toUInt(&isOk, kBaseHex); - if (type == kIgmpQuery) - { - switch(version_) - { - case 1: type = kIgmpV1Query; break; - case 2: type = kIgmpV2Query; break; - case 3: type = kIgmpV3Query; break; - } - } - igmp->set_type(type); - } - else if (name == "igmp.record_type") - { - OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); - rec->set_type(OstProto::Gmp::GroupRecord::RecordType( - valueHexStr.toUInt(&isOk, kBaseHex))); - rec->set_is_override_source_count(true); - rec->set_is_override_aux_data_length(true); - } - else if (name == "igmp.aux_data_len") - { - igmp->mutable_group_records(igmp->group_records_size() - 1)-> - set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "igmp.num_src") - { - if (igmp->group_record_count()) - igmp->mutable_group_records(igmp->group_records_size() - 1)-> - set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); - else - igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "igmp.maddr") - { - if (igmp->group_record_count()) - igmp->mutable_group_records(igmp->group_records_size() - 1)-> - mutable_group_address()->set_v4( - valueHexStr.toUInt(&isOk, kBaseHex)); - else - igmp->mutable_group_address()->set_v4( - valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "igmp.saddr") - { - if (igmp->group_record_count()) - igmp->mutable_group_records(igmp->group_records_size() - 1)-> - add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); - else - igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "igmp.aux_data") - { - QByteArray ba = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - igmp->mutable_group_records(igmp->group_records_size() - 1)-> - set_aux_data(ba.constData(), ba.size()); - } -} - -void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, - OstProto::Stream *stream) -{ - // version is 0 for IGMP like protocols such as RGMP which we don't - // support currently - if (version_ == 0) - stream->mutable_protocol()->RemoveLast(); -} - - -// ---------------------------------------------------------- // -// PdmlMldProtocol // -// ---------------------------------------------------------- // - -PdmlMldProtocol::PdmlMldProtocol() -{ - ostProtoId_ = OstProto::Protocol::kMldFieldNumber; - - fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); - fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); - fieldMap_.insert("icmpv6.mld.maximum_response_delay", - OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME - - fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); - fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); - fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME - fieldMap_.insert("icmpv6.mld.nb_sources", - OstProto::Gmp::kSourceCountFieldNumber); - - fieldMap_.insert("icmpv6.mldr.nb_mcast_records", - OstProto::Gmp::kGroupRecordCountFieldNumber); -} - -PdmlProtocol* PdmlMldProtocol::createInstance() -{ - return new PdmlMldProtocol(); -} - -void PdmlMldProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes &attributes, int /*expectedPos*/, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); - - mld->set_is_override_rsvd_code(true); - mld->set_is_override_checksum(true); - mld->set_is_override_source_count(true); - mld->set_is_override_group_record_count(true); - - protoSize_ = attributes.value("size").toString().toUInt(&isOk); -} - -void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - bool isOk; - OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); - QString valueHexStr = attributes.value("value").toString(); - - if (name == "icmpv6.type") - { - uint type = valueHexStr.toUInt(&isOk, kBaseHex); - - if ((type == kMldQuery) && (protoSize_ >= 28)) - type = kMldV2Query; - - mld->set_type(type); - } - else if (name == "icmpv6.mld.multicast_address") - { - mld->mutable_group_address()->set_v6_hi( - valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); - mld->mutable_group_address()->set_v6_lo( - valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mld.source_address") - { - OstProto::Gmp::IpAddress *ip = mld->add_sources(); - ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); - ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mldr.mar.record_type") - { - OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); - rec->set_type(OstProto::Gmp::GroupRecord::RecordType( - valueHexStr.toUInt(&isOk, kBaseHex))); - rec->set_is_override_source_count(true); - rec->set_is_override_aux_data_length(true); - } - else if (name == "icmpv6.mldr.mar.aux_data_len") - { - mld->mutable_group_records(mld->group_records_size() - 1)-> - set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mldr.mar.nb_sources") - { - mld->mutable_group_records(mld->group_records_size() - 1)-> - set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mldr.mar.multicast_address") - { - OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( - mld->group_records_size() - 1)->mutable_group_address(); - ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); - ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mldr.mar.source_address") - { - OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( - mld->group_records_size() - 1)->add_sources(); - ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); - ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); - } - else if (name == "icmpv6.mldr.mar.auxiliary_data") - { - QByteArray ba = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - mld->mutable_group_records(mld->group_records_size() - 1)-> - set_aux_data(ba.constData(), ba.size()); - } -} - - -// ---------------------------------------------------------- // -// PdmlTcpProtocol // -// ---------------------------------------------------------- // - -PdmlTcpProtocol::PdmlTcpProtocol() -{ - ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; - - fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); - fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); - fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); - fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); - fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); - fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); - fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); - fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); - fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); -} - -PdmlProtocol* PdmlTcpProtocol::createInstance() -{ - return new PdmlTcpProtocol(); -} - -void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, - int /*size*/, const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) -{ - if (name == "tcp.options") - options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - else if (name == "") - { - if (attributes.value("show").toString().startsWith("Acknowledgement number")) - { - bool isOk; - OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); - - tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); - } -#if 0 - else if (attributes.value("show").toString().startsWith("TCP segment data")) - { - segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); - stream->mutable_core()->mutable_name()->insert(0, - segmentData_.constData(), segmentData_.size()); - } -#endif - } -} - -void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); - - qDebug("Tcp: post\n"); - - tcp->set_is_override_src_port(true); - tcp->set_is_override_dst_port(true); - tcp->set_is_override_hdrlen(true); - tcp->set_is_override_cksum(true); - - if (options_.size()) - { - OstProto::Protocol *proto = stream->add_protocol(); - - proto->mutable_protocol_id()->set_id( - OstProto::Protocol::kHexDumpFieldNumber); - - OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); - - hexDump->mutable_content()->append(options_.constData(), - options_.size()); - hexDump->set_pad_until_end(false); - options_.resize(0); - } -} - -// ---------------------------------------------------------- // -// PdmlUdpProtocol // -// ---------------------------------------------------------- // - -PdmlUdpProtocol::PdmlUdpProtocol() -{ - ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; - - fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); - fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); - fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); - fieldMap_.insert("udp.checksum_coverage", - OstProto::Udp::kTotlenFieldNumber); - fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); -} - -PdmlProtocol* PdmlUdpProtocol::createInstance() -{ - return new PdmlUdpProtocol(); -} - -void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream* /*stream*/) -{ - OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); - - qDebug("Udp: post\n"); - - udp->set_is_override_src_port(true); - udp->set_is_override_dst_port(true); - udp->set_is_override_totlen(true); - udp->set_is_override_cksum(true); -} - - -// ---------------------------------------------------------- // -// PdmlTextProtocol // -// ---------------------------------------------------------- // - -PdmlTextProtocol::PdmlTextProtocol() -{ - ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; -} - -PdmlProtocol* PdmlTextProtocol::createInstance() -{ - return new PdmlTextProtocol(); -} - -void PdmlTextProtocol::preProtocolHandler(QString /*name*/, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream) -{ - bool isOk; - int size; - int pos = attributes.value("pos").toString().toUInt(&isOk); - - if (!isOk) - { - if (expectedPos >= 0) - expPos_ = pos = expectedPos; - else - goto _skip_pos_size_proc; - } - - size = attributes.value("size").toString().toUInt(&isOk); - if (!isOk) - goto _skip_pos_size_proc; - - // If pos+size goes beyond the frame length, this is a "reassembled" - // protocol and should be skipped - if ((pos + size) > int(stream->core().frame_len())) - goto _skip_pos_size_proc; - - expPos_ = pos; - endPos_ = expPos_ + size; - -_skip_pos_size_proc: - qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); - OstProto::TextProtocol *text = pbProto->MutableExtension( - OstProto::textProtocol); - - text->set_port_num(0); - text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF - - detectEol_ = true; - contentType_ = kUnknownContent; -} - -void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, - OstProto::Stream* /*stream*/) -{ -_retry: - switch(contentType_) - { - case kUnknownContent: - if (name == "data") - contentType_ = kOtherContent; - else - contentType_ = kTextContent; - goto _retry; - break; - - case kTextContent: - { - OstProto::TextProtocol *text = pbProto->MutableExtension( - OstProto::textProtocol); - - if ((name == "data") - || (attributes.value("show") == "HTTP chunked response")) - { - contentType_ = kOtherContent; - goto _retry; - } - - if (pos < expPos_) - break; - - if ((pos + size) > endPos_) - break; - - if (pos > expPos_) - { - int gap = pos - expPos_; - QByteArray filler(gap, '\n'); - - if (text->eol() == OstProto::TextProtocol::kCrLf) - { - if (gap & 0x01) // Odd - { - filler.resize(gap/2 + 1); - filler[0]=int(' '); - } - else // Even - filler.resize(gap/2); - } - - text->mutable_text()->append(filler.constData(), filler.size()); - expPos_ += gap; - } - - QByteArray line = QByteArray::fromHex( - attributes.value("value").toString().toUtf8()); - - if (detectEol_) - { - if (line.right(2) == "\r\n") - text->set_eol(OstProto::TextProtocol::kCrLf); - else if (line.right(1) == "\r") - text->set_eol(OstProto::TextProtocol::kCr); - else if (line.right(1) == "\n") - text->set_eol(OstProto::TextProtocol::kLf); - - detectEol_ = false; - } - - // Convert line endings to LF only - Qt reqmt that TextProto honours - line.replace("\r\n", "\n"); - line.replace('\r', '\n'); - - text->mutable_text()->append(line.constData(), line.size()); - expPos_ += size; - break; - } - case kOtherContent: - // Do nothing! - break; - default: - Q_ASSERT(false); - } -} - -void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream) -{ - OstProto::TextProtocol *text = pbProto->MutableExtension( - OstProto::textProtocol); - - // Empty Text Content - remove ourselves - if (text->text().length() == 0) - stream->mutable_protocol()->RemoveLast(); - - expPos_ = endPos_ = -1; - detectEol_ = true; - contentType_ = kUnknownContent; -} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h index 95a75c0..0747df1 100644 --- a/common/pdmlprotocols.h +++ b/common/pdmlprotocols.h @@ -68,246 +68,4 @@ protected: PdmlFrameProtocol(); }; -class PdmlEthProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - -protected: - PdmlEthProtocol(); -}; - -class PdmlSvlanProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); -protected: - PdmlSvlanProtocol(); -}; - -class PdmlVlanProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); -protected: - PdmlVlanProtocol(); -}; - -class PdmlLlcProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlLlcProtocol(); -}; - -class PdmlArpProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - -protected: - PdmlArpProtocol(); -}; - -class PdmlIp4Protocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlIp4Protocol(); -private: - QByteArray options_; -}; - -class PdmlIp6Protocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlIp6Protocol(); -}; - -class PdmlIcmpProtocol : public PdmlProtocol -{ - friend class PdmlIcmp6Protocol; -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlIcmpProtocol(); -private: - static const uint kIcmpInvalidType = 0xFFFFFFFF; - - static const uint kIcmp6EchoRequest = 128; - static const uint kIcmp6EchoReply = 129; -}; - -class PdmlMldProtocol : public PdmlProtocol -{ - friend class PdmlIcmp6Protocol; -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); -protected: - PdmlMldProtocol(); -private: - static const uint kMldQuery = 0x82; - static const uint kMldV1Query = 0x82; - static const uint kMldV2Query = 0xFF82; - - uint protoSize_; -}; - -class PdmlIcmp6Protocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); -protected: - PdmlIcmp6Protocol(); -private: - PdmlIcmpProtocol icmp_; - PdmlMldProtocol mld_; - PdmlProtocol *proto_; -}; - -class PdmlIgmpProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlIgmpProtocol(); -private: - static const uint kIgmpQuery = 0x11; - static const uint kIgmpV1Query = 0x11; - static const uint kIgmpV2Query = 0xFF11; - static const uint kIgmpV3Query = 0xFE11; - - uint version_; -}; - -class PdmlTcpProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlTcpProtocol(); -private: - QByteArray options_; - QByteArray segmentData_; -}; - -class PdmlUdpProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlUdpProtocol(); -}; - -class PdmlTextProtocol : public PdmlProtocol -{ -public: - static PdmlProtocol* createInstance(); - - virtual void preProtocolHandler(QString name, - const QXmlStreamAttributes &attributes, int expectedPos, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void unknownFieldHandler(QString name, int pos, int size, - const QXmlStreamAttributes &attributes, - OstProto::Protocol *pbProto, OstProto::Stream *stream); - virtual void postProtocolHandler(OstProto::Protocol *pbProto, - OstProto::Stream *stream); -protected: - PdmlTextProtocol(); -private: - enum ContentType { - kUnknownContent, - kTextContent, - kOtherContent - }; - - bool detectEol_; - ContentType contentType_; - int expPos_; - int endPos_; -}; - #endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp index c4ea3f0..7b5eb9d 100644 --- a/common/pdmlreader.cpp +++ b/common/pdmlreader.cpp @@ -26,6 +26,21 @@ along with this program. If not, see #include "pdmlprotocols.h" +#include "arppdml.h" +#include "eth2pdml.h" +#include "llcpdml.h" +#include "icmppdml.h" +#include "icmp6pdml.h" +#include "igmppdml.h" +#include "ip4pdml.h" +#include "ip6pdml.h" +#include "mldpdml.h" +#include "svlanpdml.h" +#include "tcppdml.h" +#include "textprotopdml.h" +#include "udppdml.h" +#include "vlanpdml.h" + PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { //gPdmlReader = this; diff --git a/common/svlanpdml.cpp b/common/svlanpdml.cpp new file mode 100644 index 0000000..113f515 --- /dev/null +++ b/common/svlanpdml.cpp @@ -0,0 +1,110 @@ +/* +Copyright (C) 2011 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 "svlanpdml.h" + +#include "eth2.pb.h" +#include "svlan.pb.h" + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/svlanpdml.h b/common/svlanpdml.h new file mode 100644 index 0000000..517cc0f --- /dev/null +++ b/common/svlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SVLAN_PDML_H +#define _SVLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +#endif diff --git a/common/tcppdml.cpp b/common/tcppdml.cpp new file mode 100644 index 0000000..3980b6a --- /dev/null +++ b/common/tcppdml.cpp @@ -0,0 +1,98 @@ +/* +Copyright (C) 2011 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 "tcppdml.h" + +#include "hexdump.pb.h" +#include "tcp.pb.h" + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/tcppdml.h b/common/tcppdml.h new file mode 100644 index 0000000..5c3d1c6 --- /dev/null +++ b/common/tcppdml.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TCP_PDML_H +#define _TCP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +#endif diff --git a/common/textprotopdml.cpp b/common/textprotopdml.cpp new file mode 100644 index 0000000..26ccfe7 --- /dev/null +++ b/common/textprotopdml.cpp @@ -0,0 +1,171 @@ +/* +Copyright (C) 2011 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 "textprotopdml.h" + +#include "textproto.pb.h" + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/textprotopdml.h b/common/textprotopdml.h new file mode 100644 index 0000000..daaca0c --- /dev/null +++ b/common/textprotopdml.h @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TEXT_PROTO_PDML_H +#define _TEXT_PROTO_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/udppdml.cpp b/common/udppdml.cpp new file mode 100644 index 0000000..0cb1685 --- /dev/null +++ b/common/udppdml.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 "udppdml.h" + +#include "udp.pb.h" + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + diff --git a/common/udppdml.h b/common/udppdml.h new file mode 100644 index 0000000..3ae1d6e --- /dev/null +++ b/common/udppdml.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _UDP_PDML_H +#define _UDP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +#endif diff --git a/common/vlanpdml.cpp b/common/vlanpdml.cpp new file mode 100644 index 0000000..722d038 --- /dev/null +++ b/common/vlanpdml.cpp @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 "vlanpdml.h" + +#include "eth2.pb.h" +#include "vlan.pb.h" + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/vlanpdml.h b/common/vlanpdml.h new file mode 100644 index 0000000..6bab024 --- /dev/null +++ b/common/vlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _VLAN_PDML_H +#define _VLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +#endif diff --git a/test/test.pro b/test/test.pro index 8dbe0cb..e8542e4 100644 --- a/test/test.pro +++ b/test/test.pro @@ -5,23 +5,28 @@ INCLUDEPATH += "../rpc/" "../common/" "../client" win32 { LIBS += -lwpcap -lpacket CONFIG(debug, debug|release) { - LIBS += -L"../common/debug" -lostproto + LIBS += -L"../common/debug" -lostprotogui -lostproto LIBS += -L"../rpc/debug" -lpbrpc POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ "../common/debug/libostproto.a" \ "../rpc/debug/libpbrpc.a" } else { - LIBS += -L"../common/release" -lostproto + LIBS += -L"../common/release" -lostprotogui -lostproto LIBS += -L"../rpc/release" -lpbrpc POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ "../common/release/libostproto.a" \ "../rpc/release/libpbrpc.a" } } else { LIBS += -lpcap - LIBS += -L"../common" -lostproto + LIBS += -L"../common" -lostprotogui -lostproto LIBS += -L"../rpc" -lpbrpc - POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" } LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 From 5ee478600882febf9518d9a005bb6e5034bcd402 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 2 May 2014 04:52:00 +0530 Subject: [PATCH 237/294] NOX: moved uintToHexStr() definition from AbstractProtocol to AbstractProtocolConfigForm because it is supposed to be used by config widgets in their load methods. For Mac/Arp protocols which were using it to display mac address as text a new macro uintToMacStr() was added and used --- common/abstractprotocol.h | 3 --- common/abstractprotocolconfig.h | 10 ++++++++-- common/arp.cpp | 9 +++++++-- common/mac.cpp | 10 ++++++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h index 5206662..84388ae 100644 --- a/common/abstractprotocol.h +++ b/common/abstractprotocol.h @@ -35,9 +35,6 @@ along with this program. If not, see #define BASE_DEC (10) #define BASE_HEX (16) -#define uintToHexStr(num, bytes) \ - QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) - class StreamBase; class ProtocolListIterator; diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h index 4172a7c..a65c959 100644 --- a/common/abstractprotocolconfig.h +++ b/common/abstractprotocolconfig.h @@ -23,6 +23,12 @@ along with this program. If not, see class AbstractProtocol; +/*! + Convenience Macro - can be used by loadWidget() methods +*/ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + class AbstractProtocolConfigForm : public QWidget { Q_OBJECT @@ -80,7 +86,7 @@ public: } /*! - Convenience Method - can be used by storeConfigWidget() implementations + Convenience Method - can be used by storeWidget() implementations */ uint hexStrToUInt(QString text, bool *ok=NULL) { @@ -94,7 +100,7 @@ public: } /*! - Convenience Method - can be used by storeConfigWidget() implementations + Convenience Method - can be used by storeWidget() implementations */ quint64 hexStrToUInt64(QString text, bool *ok=NULL) { diff --git a/common/arp.cpp b/common/arp.cpp index 11675a1..aee20ce 100644 --- a/common/arp.cpp +++ b/common/arp.cpp @@ -20,6 +20,11 @@ along with this program. If not, see #include "arp.h" #include +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) @@ -295,7 +300,7 @@ QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return hwAddr; case FieldTextValue: - return uintToHexStr(hwAddr, 6); + return uintToMacStr(hwAddr); case FieldFrameValue: { QByteArray fv; @@ -403,7 +408,7 @@ QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return hwAddr; case FieldTextValue: - return uintToHexStr(hwAddr, 6); + return uintToMacStr(hwAddr); case FieldFrameValue: { QByteArray fv; diff --git a/common/mac.cpp b/common/mac.cpp index ec2bcc6..70d047b 100644 --- a/common/mac.cpp +++ b/common/mac.cpp @@ -19,6 +19,12 @@ along with this program. If not, see #include "mac.h" +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() + MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { @@ -129,7 +135,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return dstMac; case FieldTextValue: - return uintToHexStr(dstMac, 6); + return uintToMacStr(dstMac); case FieldFrameValue: { QByteArray fv; @@ -174,7 +180,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, case FieldValue: return srcMac; case FieldTextValue: - return uintToHexStr(srcMac, 6); + return uintToMacStr(srcMac); case FieldFrameValue: { QByteArray fv; From 8a74987fa5c89227a53a96926665e65d2678c1b2 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 9 May 2014 07:21:42 +0530 Subject: [PATCH 238/294] NOX: Doxygen-style documentation for PdmlProtocol. Added SamplePdmlProtocol with TODO comments on the same lines as SampleProtocol --- common/abstractprotocolconfig.h | 7 +- common/ostprotogui.pro | 3 +- common/pdmlprotocol.cpp | 97 ++++++++++++++++++++++++++ common/pdmlprotocol.h | 2 + common/samplepdml.cpp | 118 ++++++++++++++++++++++++++++++++ common/samplepdml.h | 50 ++++++++++++++ 6 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 common/samplepdml.cpp create mode 100644 common/samplepdml.h diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h index a65c959..3e87aeb 100644 --- a/common/abstractprotocolconfig.h +++ b/common/abstractprotocolconfig.h @@ -63,7 +63,8 @@ public: /*! Loads data from the protocol using it's fieldData() method into this - widget + widget. Any conversion to user friendly display/editing formats (e.g. + hex format) SHOULD be done by this method. Subclasses MUST implement this function. See the SampleProtocol for an example @@ -75,7 +76,9 @@ public: /*! Stores data from this widget into the protocol using the protocol's - setFieldData() method + setFieldData() method. Field values MUST be converted from any + user friendly display/editing formats (e.g. hex format) to simple + Qt-style integers/strings before passing to setFieldData() Subclasses MUST implement this function. See the SampleProtocol for an example diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index 0bf1c3f..b075046 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -121,7 +121,8 @@ SOURCES += \ mldpdml.cpp \ tcppdml.cpp \ udppdml.cpp \ - textprotopdml.cpp + textprotopdml.cpp \ + samplepdml.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp index 2be78ab..a682aeb 100644 --- a/common/pdmlprotocol.cpp +++ b/common/pdmlprotocol.cpp @@ -19,35 +19,97 @@ along with this program. If not, see #include "pdmlprotocol.h" +/*! + \class PdmlProtocol + + PdmlProtocol is the base class which provides the interface for all + PDML decode helper protocols + + All Pdml helper classes derived from PdmlProtocol MUST register + themselves with PdmlReader. When PdmlReader encounters a 'proto' tag + in the PDML during parsing, it instantiates the corresponding helper + PdmlProtocol class and calls its methods to decode the protocol. + + A subclass MUST initialize the following inherited protected variables + in its constructor - + - ostProtoId_ + - fieldMap_ + + A subclass typically needs to reimplement the following methods - + - createInstance() + + Depending on certain conditions, subclasses may need to reimplement + the following additional methods - + - unknownFieldHandler() + - preProtocolHandler() + - postProtocolHandler() + + See the description of the methods for more information. + + Use the SamplePdmlProtocol implementation as boilerplate code and + for guidelines and tips +*/ + +/*! + Constructs the PdmlProtocol +*/ PdmlProtocol::PdmlProtocol() { ostProtoId_ = -1; } +/*! + Destroys the PdmlProtocol +*/ PdmlProtocol::~PdmlProtocol() { } +/*! + Allocates and returns a new instance of the class + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function and register it with PdmlReader +*/ PdmlProtocol* PdmlProtocol::createInstance() { return new PdmlProtocol(); } +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) +*/ int PdmlProtocol::ostProtoId() const { return ostProtoId_; } +/*! + Returns true if name is a 'known' field that can be directly mapped + to the protobuf field +*/ bool PdmlProtocol::hasField(QString name) const { return fieldMap_.contains(name); } +/*! + Returns the protocol's protobuf field number corresponding to name +*/ int PdmlProtocol::fieldId(QString name) const { return fieldMap_.value(name); } +/*! + This method is called by PdmlReader before any fields within the protocol + are processed. All attributes associated with the 'proto' tag in the PDML + are passed to this method + + Use this method to do any special handling that may be required for + preprocessing +*/ void PdmlProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, @@ -56,18 +118,41 @@ void PdmlProtocol::preProtocolHandler(QString /*name*/, return; // do nothing! } +/*! + This method is called by PdmlReader when it encounters a nested + protocol in the PDML i.e. a protocol within a protocol or a protocol + within a field + + This is a notification to the protocol that protocol processing will + be ending prematurely. postProtocolHandler() will still be called in + such cases. +*/ void PdmlProtocol::prematureEndHandler(int /*pos*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } +/*! + This method is called by PdmlReader after all fields within the protocol + are processed. + + Use this method to do any special handling that may be required for + postprocessing +*/ void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } + +/*! + This method is called by PdmlReader for each field in the protocol + + Depending on whether it is a known or unknown field, the virtual methods + knownFieldHandler() and unknownFieldHandler() are invoked +*/ void PdmlProtocol::fieldHandler(QString name, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) @@ -99,6 +184,12 @@ void PdmlProtocol::fieldHandler(QString name, } } +/*! + Handles a 'known' field + + Uses protobuf reflection interface to set the protobuf field name to + valueHexStr as per the field's datatype +*/ void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, OstProto::Protocol *pbProto) { @@ -143,6 +234,12 @@ void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, } } +/*! + Handles a 'unknown' field + + The default implementation does nothing. Subclasses may need to implement + this if the protocol contains 'unknown' fields. +*/ void PdmlProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h index 7f23524..011fcb6 100644 --- a/common/pdmlprotocol.h +++ b/common/pdmlprotocol.h @@ -59,7 +59,9 @@ public: protected: PdmlProtocol(); + //!< Protocol's field number as defined in message 'Protocol', enum 'k' int ostProtoId_; + //!< Map of PDML field names to protobuf field numbers for 'known' fields QMap fieldMap_; }; diff --git a/common/samplepdml.cpp b/common/samplepdml.cpp new file mode 100644 index 0000000..99b671b --- /dev/null +++ b/common/samplepdml.cpp @@ -0,0 +1,118 @@ +/* +Copyright (C) 2014 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 "samplepdml.h" + +#include "sample.pb.h" + +/*! + TODO : Initialize the following inherited protected members - + - ostProtoId_ + - fieldMap_ + + ostProtoId_ is the protocol's protobuf field number as defined in + message 'Protocol' enum 'k' in file protocol.proto + + fieldMap_ is a mapping of the protocol's field names as they appear + in the PDML to the protobuf field numbers for the protocol. All such + fields are classified as 'known' fields and the base class will take care + of decoding these without any help from the subclass. + + Note that the PDML field names are same as the field names used in Wireshark + display filters. The full reference for these is available at - + http://www.wireshark.org/docs/dfref/ +*/ +PdmlSampleProtocol::PdmlSampleProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + fieldMap_.insert("sample.checksum", OstProto::Sample::kChecksumFieldNumber); + fieldMap_.insert("sample.x", OstProto::Sample::kXFieldNumber); + fieldMap_.insert("sample.y", OstProto::Sample::kYFieldNumber); +} + +PdmlSampleProtocol::~PdmlSampleProtocol() +{ +} + +PdmlSampleProtocol* PdmlSampleProtocol::createInstance() +{ + return new PdmlSampleProtocol(); +} + +/*! + TODO: Use this method to do any special handling that may be required for + preprocessing a protocol before parsing/decoding the protocol's fields +*/ +void PdmlSampleProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling or cleanup that may be + required when a protocol decode is ending prematurely +*/ +void PdmlSampleProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling that may be required for + postprocessing a protocol after parsing/decoding all the protocol fields + + If your protocol's protobuf has some meta-fields that should be set to + their non default values, this is a good place to do that. e.g. derived + fields such as length, checksum etc. may be correct or incorrect in the + PCAP/PDML - to retain the same value as in the PCAP/PDML and not let + Ostinato recalculate these, you can set the is_override_length, + is_override_cksum meta-fields to true here +*/ +void PdmlSampleProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Handle all 'unknown' fields using this method + + You need to typically only handle frame fields or fields actually present + in the protocol on the wire. So you can safely ignore meta-fields such as + Good/Bad Checksum. + + Some fields may not have a 'name' attribute, so cannot be classified as + a 'known' field. Use this method to identify such fields using other + attributes such as 'show' or 'showname' and populate the corresponding + protobuf field. + + If the PDML protocol contains some fields that are not supported by Ostinato, + use a HexDump protocol as a replacement to store these bytes +*/ +void PdmlSampleProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} diff --git a/common/samplepdml.h b/common/samplepdml.h new file mode 100644 index 0000000..a07965b --- /dev/null +++ b/common/samplepdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SAMPLE_PDML_H +#define _SAMPLE_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSampleProtocol : public PdmlProtocol +{ +public: + virtual ~PdmlSampleProtocol(); + + static PdmlSampleProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlSampleProtocol(); +}; + +#endif From 1a6b23e31dda283ff678944fa02359e3e4b87aa8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 May 2014 10:12:29 +0530 Subject: [PATCH 239/294] Enabled multiple client connections to server, each in its own thread. Need to redo the thread design 'coz subclassing QThread with slots may cause problems. --- .hgignore | 33 + .vimrc | 5 + COPYING | 674 ++++++++++++ client/about.ui | 192 ++++ client/dumpview.cpp | 408 +++++++ client/dumpview.h | 61 ++ client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 91 ++ client/mainwindow.cpp | 142 +++ client/mainwindow.h | 53 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 +++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 93 ++ client/ostinato.qrc | 38 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 +++++ client/packetmodel.h | 61 ++ client/port.cpp | 573 ++++++++++ client/port.h | 147 +++ client/portconfigdialog.cpp | 57 + client/portconfigdialog.h | 39 + client/portconfigdialog.ui | 109 ++ client/portgroup.cpp | 838 ++++++++++++++ client/portgroup.h | 145 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 339 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 +++ client/portstatsfilterdialog.cpp | 131 +++ client/portstatsfilterdialog.h | 54 + client/portstatsmodel.cpp | 326 ++++++ client/portstatsmodel.h | 151 +++ client/portstatswindow.cpp | 180 +++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 +++ client/portswindow.cpp | 663 +++++++++++ client/portswindow.h | 86 ++ client/portswindow.ui | 299 +++++ client/preferences.cpp | 119 ++ client/preferences.h | 45 + client/preferences.ui | 226 ++++ client/settings.h | 86 ++ client/stream.cpp | 51 + client/stream.h | 42 + client/streamconfigdialog.cpp | 1203 ++++++++++++++++++++ client/streamconfigdialog.h | 150 +++ client/streamconfigdialog.ui | 1462 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 288 +++++ client/streammodel.h | 78 ++ common/abstractfileformat.cpp | 127 +++ common/abstractfileformat.h | 91 ++ common/abstractprotocol.cpp | 898 +++++++++++++++ common/abstractprotocol.h | 160 +++ common/abstractprotocolconfig.h | 120 ++ common/arp.cpp | 825 ++++++++++++++ common/arp.h | 103 ++ common/arp.proto | 67 ++ common/arp.ui | 518 +++++++++ common/arpconfig.cpp | 270 +++++ common/arpconfig.h | 46 + common/arppdml.cpp | 41 + common/arppdml.h | 34 + common/comboprotocol.h | 190 ++++ common/comboprotocolconfig.h | 100 ++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2llcconfig.h | 36 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot2snapconfig.h | 36 + common/dot3.cpp | 196 ++++ common/dot3.h | 67 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/dot3config.cpp | 63 ++ common/dot3config.h | 41 + common/eth2.cpp | 188 ++++ common/eth2.h | 66 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/eth2config.cpp | 63 ++ common/eth2config.h | 41 + common/eth2pdml.cpp | 124 +++ common/eth2pdml.h | 38 + common/fileformat.cpp | 483 ++++++++ common/fileformat.h | 62 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 769 +++++++++++++ common/gmp.h | 129 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 ++++++++++++++ common/gmpconfig.cpp | 384 +++++++ common/gmpconfig.h | 63 ++ common/hexdump.cpp | 211 ++++ common/hexdump.h | 73 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/hexdumpconfig.cpp | 75 ++ common/hexdumpconfig.h | 44 + common/icmp.cpp | 396 +++++++ common/icmp.h | 96 ++ common/icmp.proto | 44 + common/icmp.ui | 178 +++ common/icmp6pdml.cpp | 100 ++ common/icmp6pdml.h | 50 + common/icmpconfig.cpp | 191 ++++ common/icmpconfig.h | 50 + common/icmphelper.h | 88 ++ common/icmppdml.cpp | 93 ++ common/icmppdml.h | 48 + common/igmp.cpp | 364 ++++++ common/igmp.h | 98 ++ common/igmp.proto | 27 + common/igmpconfig.cpp | 112 ++ common/igmpconfig.h | 40 + common/igmppdml.cpp | 141 +++ common/igmppdml.h | 49 + common/intcombobox.h | 69 ++ common/ip4.cpp | 846 ++++++++++++++ common/ip4.h | 99 ++ common/ip4.proto | 66 ++ common/ip4.ui | 516 +++++++++ common/ip4config.cpp | 297 +++++ common/ip4config.h | 44 + common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over4config.h | 35 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip4over6config.h | 35 + common/ip4pdml.cpp | 93 ++ common/ip4pdml.h | 41 + common/ip6.cpp | 806 ++++++++++++++ common/ip6.h | 112 ++ common/ip6.proto | 61 ++ common/ip6.ui | 467 ++++++++ common/ip6config.cpp | 233 ++++ common/ip6config.h | 44 + common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over4config.h | 35 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/ip6over6config.h | 33 + common/ip6pdml.cpp | 76 ++ common/ip6pdml.h | 39 + common/iputils.h | 122 +++ common/ipv4addressdelegate.h | 58 + common/ipv6addressdelegate.h | 60 + common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 264 +++++ common/llc.h | 71 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/llcconfig.cpp | 100 ++ common/llcconfig.h | 41 + common/llcpdml.cpp | 80 ++ common/llcpdml.h | 39 + common/mac.cpp | 351 ++++++ common/mac.h | 73 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/macconfig.cpp | 148 +++ common/macconfig.h | 45 + common/mld.cpp | 543 +++++++++ common/mld.h | 95 ++ common/mld.proto | 27 + common/mldconfig.cpp | 109 ++ common/mldconfig.h | 41 + common/mldpdml.cpp | 133 +++ common/mldpdml.h | 47 + common/ostproto.pro | 111 ++ common/ostprotogui.pro | 129 +++ common/ostprotolib.cpp | 55 + common/ostprotolib.h | 42 + common/payload.cpp | 258 +++++ common/payload.h | 71 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/payloadconfig.cpp | 84 ++ common/payloadconfig.h | 44 + common/pcapfileformat.cpp | 661 +++++++++++ common/pcapfileformat.h | 87 ++ common/pcapfileimport.ui | 132 +++ common/pdmlfileformat.cpp | 163 +++ common/pdmlfileformat.h | 42 + common/pdmlprotocol.cpp | 248 +++++ common/pdmlprotocol.h | 68 ++ common/pdmlprotocols.cpp | 195 ++++ common/pdmlprotocols.h | 71 ++ common/pdmlreader.cpp | 548 +++++++++ common/pdmlreader.h | 75 ++ common/protocol.proto | 249 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 232 ++++ common/protocolmanager.h | 58 + common/protocolwidgetfactory.cpp | 193 ++++ common/protocolwidgetfactory.h | 46 + common/sample.cpp | 477 ++++++++ common/sample.h | 89 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/sampleconfig.cpp | 117 ++ common/sampleconfig.h | 43 + common/samplepdml.cpp | 118 ++ common/samplepdml.h | 50 + common/snap.cpp | 255 +++++ common/snap.h | 70 ++ common/snap.proto | 34 + common/snap.ui | 122 +++ common/snapconfig.cpp | 78 ++ common/snapconfig.h | 41 + common/streambase.cpp | 565 ++++++++++ common/streambase.h | 150 +++ common/svlan.cpp | 66 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/svlanconfig.h | 28 + common/svlanpdml.cpp | 110 ++ common/svlanpdml.h | 40 + common/tcp.cpp | 606 ++++++++++ common/tcp.h | 89 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/tcpconfig.cpp | 175 +++ common/tcpconfig.h | 41 + common/tcppdml.cpp | 98 ++ common/tcppdml.h | 42 + common/textproto.cpp | 240 ++++ common/textproto.h | 77 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/textprotoconfig.cpp | 84 ++ common/textprotoconfig.h | 41 + common/textprotopdml.cpp | 171 +++ common/textprotopdml.h | 53 + common/udp.cpp | 431 ++++++++ common/udp.h | 75 ++ common/udp.proto | 39 + common/udp.ui | 174 +++ common/udpconfig.cpp | 114 ++ common/udpconfig.h | 41 + common/udppdml.cpp | 53 + common/udppdml.h | 35 + common/userscript.cpp | 555 ++++++++++ common/userscript.h | 162 +++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/userscriptconfig.cpp | 109 ++ common/userscriptconfig.h | 51 + common/vlan.cpp | 270 +++++ common/vlan.h | 67 ++ common/vlan.proto | 34 + common/vlan.ui | 181 +++ common/vlanconfig.cpp | 87 ++ common/vlanconfig.h | 41 + common/vlanpdml.cpp | 91 ++ common/vlanpdml.h | 40 + common/vlanstack.h | 30 + common/vlanstack.proto | 31 + common/vlanstackconfig.h | 38 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 12 + extra/qhexedit2/src/commands.cpp | 115 ++ extra/qhexedit2/src/commands.h | 70 ++ extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 152 +++ extra/qhexedit2/src/qhexedit.h | 205 ++++ extra/qhexedit2/src/qhexedit_p.cpp | 800 ++++++++++++++ extra/qhexedit2/src/qhexedit_p.h | 120 ++ extra/qhexedit2/src/xbytearray.cpp | 167 +++ extra/qhexedit2/src/xbytearray.h | 66 ++ install.pri | 14 + ost.pro | 9 + protobuf.pri | 33 + rpc/pbhelper.h | 170 +++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 338 ++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 39 + rpc/pbrpccontroller.h | 72 ++ rpc/rpcserver.cpp | 57 + rpc/rpcserver.h | 83 ++ rpc/rpcthread.cpp | 272 +++++ rpc/rpcthread.h | 72 ++ server/abstractport.cpp | 594 ++++++++++ server/abstractport.h | 127 +++ server/bsdport.cpp | 355 ++++++ server/bsdport.h | 61 ++ server/drone.cpp | 53 + server/drone.h | 40 + server/drone.pro | 47 + server/drone_main.cpp | 83 ++ server/icons/portgroup.png | Bin 0 -> 667 bytes server/linuxport.cpp | 770 +++++++++++++ server/linuxport.h | 68 ++ server/myservice.cpp | 475 ++++++++ server/myservice.h | 106 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 792 ++++++++++++++ server/pcapport.h | 217 ++++ server/portmanager.cpp | 94 ++ server/portmanager.h | 43 + server/winpcapport.cpp | 226 ++++ server/winpcapport.h | 56 + test/main.cpp | 109 ++ test/test.pro | 39 + version.pri | 19 + 357 files changed, 49845 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portconfigdialog.cpp create mode 100644 client/portconfigdialog.h create mode 100644 client/portconfigdialog.ui create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/abstractprotocolconfig.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/arpconfig.cpp create mode 100644 common/arpconfig.h create mode 100644 common/arppdml.cpp create mode 100644 common/arppdml.h create mode 100644 common/comboprotocol.h create mode 100644 common/comboprotocolconfig.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2llcconfig.h create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot2snapconfig.h create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/dot3config.cpp create mode 100644 common/dot3config.h create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/eth2config.cpp create mode 100644 common/eth2config.h create mode 100644 common/eth2pdml.cpp create mode 100644 common/eth2pdml.h create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/gmpconfig.cpp create mode 100644 common/gmpconfig.h create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/hexdumpconfig.cpp create mode 100644 common/hexdumpconfig.h create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/icmp6pdml.cpp create mode 100644 common/icmp6pdml.h create mode 100644 common/icmpconfig.cpp create mode 100644 common/icmpconfig.h create mode 100644 common/icmphelper.h create mode 100644 common/icmppdml.cpp create mode 100644 common/icmppdml.h create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/igmpconfig.cpp create mode 100644 common/igmpconfig.h create mode 100644 common/igmppdml.cpp create mode 100644 common/igmppdml.h create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4config.cpp create mode 100644 common/ip4config.h create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over4config.h create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip4over6config.h create mode 100644 common/ip4pdml.cpp create mode 100644 common/ip4pdml.h create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6config.cpp create mode 100644 common/ip6config.h create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over4config.h create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/ip6over6config.h create mode 100644 common/ip6pdml.cpp create mode 100644 common/ip6pdml.h create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/llcconfig.cpp create mode 100644 common/llcconfig.h create mode 100644 common/llcpdml.cpp create mode 100644 common/llcpdml.h create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/macconfig.cpp create mode 100644 common/macconfig.h create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/mldconfig.cpp create mode 100644 common/mldconfig.h create mode 100644 common/mldpdml.cpp create mode 100644 common/mldpdml.h create mode 100644 common/ostproto.pro create mode 100644 common/ostprotogui.pro create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/payloadconfig.cpp create mode 100644 common/payloadconfig.h create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h create mode 100644 common/pcapfileimport.ui create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlprotocols.cpp create mode 100644 common/pdmlprotocols.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/protocolwidgetfactory.cpp create mode 100644 common/protocolwidgetfactory.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/sampleconfig.cpp create mode 100644 common/sampleconfig.h create mode 100644 common/samplepdml.cpp create mode 100644 common/samplepdml.h create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/snapconfig.cpp create mode 100644 common/snapconfig.h create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/svlanconfig.h create mode 100644 common/svlanpdml.cpp create mode 100644 common/svlanpdml.h create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/tcpconfig.cpp create mode 100644 common/tcpconfig.h create mode 100644 common/tcppdml.cpp create mode 100644 common/tcppdml.h create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/textprotoconfig.cpp create mode 100644 common/textprotoconfig.h create mode 100644 common/textprotopdml.cpp create mode 100644 common/textprotopdml.h create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/udpconfig.cpp create mode 100644 common/udpconfig.h create mode 100644 common/udppdml.cpp create mode 100644 common/udppdml.h create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/userscriptconfig.cpp create mode 100644 common/userscriptconfig.h create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanconfig.cpp create mode 100644 common/vlanconfig.h create mode 100644 common/vlanpdml.cpp create mode 100644 common/vlanpdml.h create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 common/vlanstackconfig.h create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/commands.cpp create mode 100644 extra/qhexedit2/src/commands.h create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 extra/qhexedit2/src/xbytearray.cpp create mode 100644 extra/qhexedit2/src/xbytearray.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 rpc/rpcthread.cpp create mode 100644 rpc/rpcthread.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/bsdport.cpp create mode 100644 server/bsdport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/linuxport.cpp create mode 100644 server/linuxport.h create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 test/main.cpp create mode 100644 test/test.pro create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..cd5490f --- /dev/null +++ b/.hgignore @@ -0,0 +1,33 @@ +syntax: glob + +# generated object files +*.o +*.a +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc + +# ostinato generated files +version.cpp + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..4727324 --- /dev/null +++ b/client/about.ui @@ -0,0 +1,192 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2012 Srivats P. + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
    G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/ostprotolib.h" +#include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" +#include "settings.h" + +#include +#include +#include + +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; + +QSettings *appSettings; +QMainWindow *mainWindow; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + OstProtocolWidgetFactory = new ProtocolWidgetFactory(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + + delete mainWindow; + delete appSettings; + delete OstProtocolManager; + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..79cd708 --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,142 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "settings.h" +#include "ui_about.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); + statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ +#ifdef Q_OS_WIN32 + //! \todo - find a way to terminate cleanly + localServer_->kill(); +#else + localServer_->terminate(); +#endif + + delete pgl; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); + + localServer_->waitForFinished(); + delete localServer_; +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2f2602d --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,53 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..d5fd459 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,93 @@ +TEMPLATE = app +CONFIG += qt +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostprotogui -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostprotogui -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostprotogui -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portconfigdialog.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portconfigdialog.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portconfigdialog.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..6162ac0 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,38 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/gaps.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..7f1b4c9 --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,573 @@ +/* +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 +*/ + +#include "port.h" + +#include "abstractfileformat.h" + +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; + +uint Port::mAllocStreamId = 0; + +static const int kEthOverhead = 20; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +void Port::recalculateAverageRates() +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAveragePacketRate(double packetsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) * + (packetsPerSec - avgPacketsPerSec_)); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); +} + +void Port::setAverageBitRate(double bitsPerSec) +{ + double rate; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); + qDebug("rate = %g", rate); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) + * ((bitsPerSec - avgBitsPerSec_) + / ((s->frameLenAvg()+kEthOverhead)*8))); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + emit portRateChanged(mPortGroupId, mPortId); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + bool ret = false; + QDialog *optDialog; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); + OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); + + if (fmt == NULL) + goto _fail; + + if ((optDialog = fmt->openOptionsDialog())) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) + goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); + + if (!append) + { + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + } + + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); + for (int i = 0; i < streams.stream_size(); i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + +_user_cancel: + emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: + ret = true; + +_fail: + progress.close(); + mainWindow->setEnabled(true); + recalculateAverageRates(); + return ret; +} + +bool Port::saveStreams(QString fileName, QString fileType, QString &error) +{ + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); + OstProto::StreamConfigList streams; + + if (fmt == NULL) + goto _fail; + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..10c2803 --- /dev/null +++ b/client/port.h @@ -0,0 +1,147 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + double avgPacketsPerSec_; + double avgBitsPerSec_; + int numActiveStreams_; + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + OstProto::TransmitMode transmitMode() + { return d.transmit_mode(); } + double averagePacketRate() + { return avgPacketsPerSec_; } + double averageBitRate() + { return avgBitsPerSec_; } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void setAveragePacketRate(double packetsPerSec); + void setAverageBitRate(double bitsPerSec); + // FIXME(MED): Bad Hack! port should not need an external trigger to + // recalculate - refactor client side domain objects and model objects + void recalculateAverageRates(); + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); + +signals: + void portRateChanged(int portGroupId, int portId); + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portconfigdialog.cpp b/client/portconfigdialog.cpp new file mode 100644 index 0000000..17adeb5 --- /dev/null +++ b/client/portconfigdialog.cpp @@ -0,0 +1,57 @@ +/* +Copyright (C) 2011 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 "portconfigdialog.h" + +PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent) + : QDialog(parent), portConfig_(portConfig) +{ + qDebug("In %s", __FUNCTION__); + + setupUi(this); + + switch(portConfig_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + sequentialStreamsButton->setChecked(true); + break; + case OstProto::kInterleavedTransmit: + interleavedStreamsButton->setChecked(true); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } + + exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); +} + +void PortConfigDialog::accept() +{ + if (sequentialStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kSequentialTransmit); + else if (interleavedStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit); + else + Q_ASSERT(false); // Unreachable!!! + + portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked()); + + QDialog::accept(); +} diff --git a/client/portconfigdialog.h b/client/portconfigdialog.h new file mode 100644 index 0000000..4d14b0b --- /dev/null +++ b/client/portconfigdialog.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PORT_CONFIG_DIALOG_H +#define _PORT_CONFIG_DIALOG_H + +#include "ui_portconfigdialog.h" +#include "protocol.pb.h" +#include + +class PortConfigDialog : public QDialog, public Ui::PortConfigDialog +{ +public: + PortConfigDialog(OstProto::Port &portConfig, QWidget *parent); + +private: + virtual void accept(); + + OstProto::Port &portConfig_; +}; + +#endif + diff --git a/client/portconfigdialog.ui b/client/portconfigdialog.ui new file mode 100644 index 0000000..954b827 --- /dev/null +++ b/client/portconfigdialog.ui @@ -0,0 +1,109 @@ + + PortConfigDialog + + + + 0 + 0 + 244 + 160 + + + + Port Config + + + + + + Transmit Mode + + + + + + Sequential Streams + + + true + + + + + + + Interleaved Streams + + + + + + + + + + Exclusive Control + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PortConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..9762fac --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,838 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting portlist ..."); + + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://code.google.com/p/ostinato/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->CopyFrom(portConfig); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + + emit portGroupDataChanged(mPortGroupId, id); + } + + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTx(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed", __FUNCTION__); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..b4d7580 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,145 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { reconnect = true; rpcChannel->establish(); } + void connectToHost(QHostAddress ip, quint16 port) + { reconnect = true; rpcChannel->establish(ip, port); } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const + { return rpcChannel->state(); } + + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, OstProto::Port portConfig); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..0ef055e --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,339 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + qDebug("portGroupId = %d, portId = %d", portGroupId, portId); + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..55882f1 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,131 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + 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); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + 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() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..7eea255 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,54 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + 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(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..cd53d4d --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,326 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + case e_STAT_RX_DROPS : return quint64(stats.rx_drops()); + case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); + case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); + case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors()); + + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2") + .arg(pgl->mPortGroups.at(portGroupIdx)->id()) + .arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id()); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..a0d6868 --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + // Rx Errors + e_STAT_RX_DROPS, + e_STAT_RX_ERRORS, + e_STAT_RX_FIFO_ERRORS, + e_STAT_RX_FRAME_ERRORS, + + e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif + << "Receive Drops" + << "Receive Errors" + << "Receive Fifo Errors" + << "Receive Frame Errors" +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..035a23c --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,180 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(0).clearPortStats(); + } +} + +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) + { + // hide/show sections first ... + for(int i = 0; i < model->columnCount(); i++) + tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + + // ... then for the 'shown' columns, set the visual index + for(int i = 0; i < newColumns.size(); i++) + { + tvPortStats->horizontalHeader()->moveSection(tvPortStats-> + horizontalHeader()->visualIndex(newColumns.at(i)), i); + } + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..1f3bc4d --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,663 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include "abstractfileformat.h" +#include "portconfigdialog.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +#include +#include +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + tvPortList->addAction(actionPort_Configuration); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + connect(plm->getStreamModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(streamModelDataChanged())); + connect(plm->getStreamModel(), + SIGNAL(modelReset()), + this, SLOT(streamModelDataChanged())); +} + +void PortsWindow::streamModelDataChanged() +{ + if (plm->isPort(tvPortList->currentIndex())) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + int ret; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + ret = scd->exec(); + + if (ret == QDialog::Accepted) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); + + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + qDebug("In %s", __FUNCTION__); + + if (previous.isValid() && plm->isPort(previous)) + { + disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), + this, SLOT(updatePortRates())); + } + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + updatePortRates(); + connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), + SLOT(updatePortRates())); + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + qDebug("In %s", __FUNCTION__); +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + // Update UI to reflect potential change in exclusive mode, + // transmit mode et al + when_portView_currentChanged(tvPortList->currentIndex(), + tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::on_averagePacketsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); + + plm->port(current).setAveragePacketRate(pps); +} + +void PortsWindow::on_averageBitsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); + + plm->port(current).setAverageBitRate(bps); +} + +void PortsWindow::updatePortRates() +{ + QModelIndex current = tvPortList->currentIndex(); + + if (!current.isValid()) + return; + + if (!plm->isPort(current)) + return; + + averagePacketsPerSec->setText(QString("%L1") + .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); + averageBitsPerSec->setText(QString("%L1") + .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + actionPort_Configuration->setEnabled(true); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + { + OstProto::Port config; + + config.set_is_exclusive_control(checked); + plm->portGroup(current.parent()).modifyPort(current.row(), config); + } +} + +void PortsWindow::on_actionPort_Configuration_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (!plm->isPort(current)) + return; + + OstProto::Port config; + config.set_transmit_mode(plm->port(current).transmitMode()); + config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); + + PortConfigDialog dialog(config, this); + + if (dialog.exec() == QDialog::Accepted) + plm->portGroup(current.parent()).modifyPort(current.row(), config); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; + QString fileName; + QString errorStr; + bool append = true; + bool ret; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + dirName = QFileInfo(fileName).absolutePath(); + +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; + QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need.On some Linux distros the native dialog can't + // distinguish between Ostinato(*) and PCAP(*) +#if defined(Q_OS_MAC) || defined(Q_OS_UNIX) + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); + + Q_ASSERT(plm->isPort(current)); + +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); + if (fileName.isEmpty()) + goto _exit; + + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..5c071f0 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_averagePacketsPerSec_editingFinished(); + void on_averageBitsPerSec_editingFinished(); + void updatePortRates(); + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + void on_actionPort_Configuration_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); + + void streamModelDataChanged(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a5d9261 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,299 @@ + + PortsWindow + + + + 0 + 0 + 710 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Avg pps + + + true + + + + + + + + + + Avg bps + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 1 + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + Port Configuration ... + + + + + + + + + radioButton + toggled(bool) + averagePacketsPerSec + setEnabled(bool) + + + 313 + 28 + + + 380 + 28 + + + + + radioButton_2 + toggled(bool) + averageBitsPerSec + setEnabled(bool) + + + 333 + 55 + + + 395 + 56 + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..0e54bbd --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,119 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "../common/ostprotolib.h" +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..78109ab --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..d64b4cb --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,226 @@ + + Preferences + + + + 0 + 0 + 400 + 220 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 'wireshark' Path + + + wiresharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + Qt::Vertical + + + + 21 + 61 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..d76bb47 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..2305930 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,51 @@ +/* +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 +*/ + +/*! + * \todo Remove this class + */ + +#include +#include + +#include "stream.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; +} + +void Stream::storeProtocolWidgets() +{ + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..8752f21 --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1203 @@ +/* +Copyright (C) 2010-2011 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 + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "abstractprotocolconfig.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +#include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" + +extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; + +QRect StreamConfigDialog::lastGeometry; +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; + +static const uint kEthFrameOverHead = 20; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setHidden(true); + pbNext->setHidden(true); + //! \todo Support Goto Stream Id + leStreamId->setHidden(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + + switch(mPort.transmitMode()) + { + case OstProto::kSequentialTransmit: + rbModeFixed->setChecked(true); + rbModeContinuous->setDisabled(true); + break; + case OstProto::kInterleavedTransmit: + rbModeContinuous->setChecked(true); + rbModeFixed->setDisabled(true); + + nextWhat->setHidden(true); + break; + default: + Q_ASSERT(false); // Unreachable + } + + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + foreach (AbstractProtocolConfigForm* w, _protocolWidgets) { + OstProtocolWidgetFactory->deleteConfigWidget(w); + } + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::loadProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: Protocol Widgets are created on demand. Once created we + // store them in _protocolWidgets indexed by the protocol + // object's address (to ensure unique widgets for multiple + // objects of the same class). Subsequently we check + // _protocolWidgets before creating a new widget + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + _protocolWidgets.insert(p, w); + } + w->loadWidget(p); + } + delete iter; +} + +void StreamConfigDialog::storeProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: After creating a widget, we need to call loadWidget() + // to load the protocol's default values + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + w->storeWidget(p); + } + delete iter; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets - create new ones, if required + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + tbProtocolData->addItem(w, p->name()); + } + + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); + + tbProtocolData->show(); + break; + } + + // Stream Control + case 2: + { + StoreCurrentStream(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText( + QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); + leBurstsPerSec->setText( + QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + + leGapIsg->setText("0.0"); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate( + QLocale().toDouble(lePacketsPerSec->text(), &isOk)); + pStream->setBurstRate( + QLocale().toDouble(leBurstsPerSec->text(), &isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + storeProtocolWidgets(); + loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) +{ + if (checked) + on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); +} + +void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) +{ + if (checked) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) +{ + if (rbSendBursts->isChecked()) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); + + if (rbPacketsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("0.0")); + leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); + } +} + +void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = burstsPerSec * + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + if (rbBurstsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); + leGapIpg->setText(QString("0.0")); + } + qDebug("end of %s", __FUNCTION__); +} + +void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk)/ + double((frameLen+kEthFrameOverHead)*8); + lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); + } + else if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk)/ + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); + } +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) + && (mpStream->isFrameVariable())) + { + log += "In 'Interleaved Streams' transmit mode, the count for " + "varying fields at transmit time may not be same as configured\n"; + } + + mpStream->preflightCheck(log); + + if (log.length()) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastGeometry = geometry(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..5330414 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + +class AbstractProtocolConfigForm; + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + QHash _protocolWidgets; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + void loadProtocolWidgets(); + void storeProtocolWidgets(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + // "Stream Control" related + void on_rbPacketsPerSec_toggled(bool checked); + void on_rbBurstsPerSec_toggled(bool checked); + + void on_lePacketsPerBurst_textChanged(const QString &text); + void on_lePacketsPerSec_textChanged(const QString &text); + void on_leBurstsPerSec_textChanged(const QString &text); + void on_leBitsPerSec_textEdited(const QString &text); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..be71e47 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1462 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumBursts + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + lePacketsPerBurst + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Bursts/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bits/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto First + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + + 20 + 41 + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + true + + + Gaps (in seconds) + + + false + + + false + + + + + + + + + :/icons/gaps.png + + + + + + + ISG + + + leGapIsg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IBG + + + leGapIbg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leGapIpg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 153 + 21 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
    dumpview.h
    + 1 +
    +
    + + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbVlanNone + rbVlanSingle + rbVlanDouble + rbL3None + rbL3Ipv4 + rbL3Ipv6 + rbL3Arp + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Tcp + rbL4Udp + rbL4Other + rbPayloadPattern + rbPayloadOther + pbPrev + pbNext + pbOk + pbCancel + rbSendBursts + leNumPackets + leNumBursts + lePacketsPerBurst + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + rbModeFixed + rbModeContinuous + lePacketsPerSec + leBurstsPerSec + leGapIsg + leGapIpg + leGapIbg + tvPacketTree + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + tbUp + lvAllProtocols + tbAdd + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 623 + 496 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 463 + 143 + + + 463 + 177 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setEnabled(bool) + + + 30 + 68 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setEnabled(bool) + + + 30 + 95 + + + 299 + 132 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 30 + 95 + + + 134 + 189 + + + + + rbPacketsPerSec + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 299 + 82 + + + 299 + 108 + + + + + rbBurstsPerSec + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 299 + 132 + + + 299 + 158 + + + + + rbBitsPerSec + toggled(bool) + leBitsPerSec + setEnabled(bool) + + + 299 + 182 + + + 299 + 208 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setChecked(bool) + + + 95 + 70 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setChecked(bool) + + + 96 + 98 + + + 299 + 132 + + + + + rbModeContinuous + toggled(bool) + leNumPackets + setDisabled(bool) + + + 73 + 196 + + + 164 + 108 + + + + + rbModeContinuous + toggled(bool) + leNumBursts + setDisabled(bool) + + + 96 + 199 + + + 226 + 155 + + + + +
    diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..c66f02c --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,288 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + int count = StreamMaxFields; + if (mCurrentPort && + (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) + count--; + + return count; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..d559618 --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamStatus, + StreamName, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..15271d7 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ + stop_ = false; +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..1f8447d --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class AbstractFileFormat : public QThread +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif + diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..230fd81 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,898 @@ +/* +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 +*/ + +#include "abstractprotocol.h" + +#include "protocollistiterator.h" +#include "streambase.h" + +#include + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + - protocolFrameVariableCount() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function + + \todo convert this to a protected data member instead of a virtual function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return (protocolFrameVariableCount() > 1); +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns the minimum number of frames required for the protocol to + vary its fields + + This is the lowest common multiple (LCM) of the counts of all the varying + fields in the protocol. Use the AbstractProtocol::lcm() static utility + function to calculate the LCM. + + The default implementation returns 1 implying that the protocol has no + varying fields. A subclass should reimplement if it has varying fields + e.g. an IP protocol that increments/decrements the IP address with + every packet +*/ +int AbstractProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +int AbstractProtocol::protocolFramePayloadVariableCount() const +{ + int count = 1; + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable() + || p->isProtocolFrameSizeVariable()) + count = lcm(count, p->protocolFrameVariableCount()); + p = p->next; + } + if (parent && (parent->isProtocolFramePayloadValueVariable() + || parent->isProtocolFramePayloadSizeVariable())) + count = lcm(count, parent->protocolFramePayloadVariableCount()); + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +// Stein's binary GCD algo - from wikipedia +quint64 AbstractProtocol::gcd(quint64 u, quint64 v) +{ + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Let shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + quint64 diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +quint64 AbstractProtocol::lcm(quint64 u, quint64 v) +{ +#if 0 + /* LCM(0,x) := x */ + if (u == 0 || v == 0) + return u | v; +#else + /* For our use case, neither u nor v can ever be 0, the minimum + value is 1; we do this correction silently here */ + if (u == 0) u = 1; + if (v == 0) v = 1; + + if (u == 1 || v == 1) + return (u * v); +#endif + + return (u * v)/gcd(u, v); +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..84388ae --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,160 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + static quint64 lcm(quint64 u, quint64 v); + static quint64 gcd(quint64 u, quint64 v); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h new file mode 100644 index 0000000..3e87aeb --- /dev/null +++ b/common/abstractprotocolconfig.h @@ -0,0 +1,120 @@ +/* +Copyright (C) 2013-2014 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 +*/ +#ifndef _ABSTRACT_PROTOCOL_CONFIG_H +#define _ABSTRACT_PROTOCOL_CONFIG_H + +#include + +class AbstractProtocol; + +/*! + Convenience Macro - can be used by loadWidget() methods +*/ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class AbstractProtocolConfigForm : public QWidget +{ + Q_OBJECT +public: +/*! + Constructs the widget +*/ + AbstractProtocolConfigForm(QWidget *parent = 0) + : QWidget(parent) + { + // Do nothing! + } + +/*! + Destroys the widget +*/ + virtual ~AbstractProtocolConfigForm() + { + // Do nothing! + } + +/*! + Allocates and returns a new instance of the widget. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ + static AbstractProtocolConfigForm* createInstance() + { + return NULL; + } + +/*! + Loads data from the protocol using it's fieldData() method into this + widget. Any conversion to user friendly display/editing formats (e.g. + hex format) SHOULD be done by this method. + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void loadWidget(AbstractProtocol *proto) + { + // Do nothing! + } + +/*! + Stores data from this widget into the protocol using the protocol's + setFieldData() method. Field values MUST be converted from any + user friendly display/editing formats (e.g. hex format) to simple + Qt-style integers/strings before passing to setFieldData() + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void storeWidget(AbstractProtocol *proto) + { + // Do nothing! + } + +/*! + Convenience Method - can be used by storeWidget() implementations +*/ + uint hexStrToUInt(QString text, bool *ok=NULL) + { + bool isOk; + uint a_uint = text.remove(QChar(' ')).toUInt(&isOk, 16); + + if (ok) + *ok = isOk; + + return a_uint; + } + +/*! + Convenience Method - can be used by storeWidget() implementations +*/ + quint64 hexStrToUInt64(QString text, bool *ok=NULL) + { + bool isOk; + quint64 a_uint = text.remove(QChar(' ')).toULongLong(&isOk, 16); + + if (ok) + *ok = isOk; + + return a_uint; + } +}; + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..aee20ce --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,825 @@ +/* +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 +*/ + +#include "arp.h" + +#include +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; +} + +ArpProtocol::~ArpProtocol() +{ +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToMacStr(hwAddr); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToMacStr(hwAddr); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + return true; + + return false; +} + +int ArpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); + } + + return count; +} diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..6b674f9 --- /dev/null +++ b/common/arp.h @@ -0,0 +1,103 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "abstractprotocol.h" +#include "arp.pb.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpProtocol : public AbstractProtocol +{ +public: + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Arp data; +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
    diff --git a/common/arpconfig.cpp b/common/arpconfig.cpp new file mode 100644 index 0000000..e24dfe4 --- /dev/null +++ b/common/arpconfig.cpp @@ -0,0 +1,270 @@ +/* +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 +*/ + +#include "arpconfig.h" +#include "arp.h" + +#include + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +ArpConfigForm::~ArpConfigForm() +{ +} + +ArpConfigForm* ArpConfigForm::createInstance() +{ + return new ArpConfigForm; +} + +void ArpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hwType->setText( + proto->fieldData( + ArpProtocol::arp_hwType, + AbstractProtocol::FieldValue + ).toString()); + protoType->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_protoType, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + hwAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_hwAddrLen, + AbstractProtocol::FieldValue + ).toString()); + protoAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_protoAddrLen, + AbstractProtocol::FieldValue + ).toString()); + + opCodeCombo->setValue( + proto->fieldData( + ArpProtocol::arp_opCode, + AbstractProtocol::FieldValue + ).toUInt()); + + senderHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_senderHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + senderHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + senderProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + senderProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + senderProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + targetHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_targetHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + targetHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + targetProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + targetProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + targetProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void ArpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + ArpProtocol::arp_hwType, + hwType->text()); + proto->setFieldData( + ArpProtocol::arp_protoType, + hexStrToUInt(protoType->text())); + proto->setFieldData( + ArpProtocol::arp_hwAddrLen, + hwAddrLen->text()); + proto->setFieldData( + ArpProtocol::arp_protoAddrLen, + protoAddrLen->text()); + + proto->setFieldData( + ArpProtocol::arp_opCode, + opCodeCombo->currentValue()); + + proto->setFieldData( + ArpProtocol::arp_senderHwAddr, + hexStrToUInt64(senderHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrMode, + senderHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrCount, + senderHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_senderProtoAddr, + QHostAddress(senderProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMode, + senderProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrCount, + senderProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMask, + QHostAddress(senderProtoAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + ArpProtocol::arp_targetHwAddr, + hexStrToUInt64(targetHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrMode, + targetHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrCount, + targetHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_targetProtoAddr, + QHostAddress(targetProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMode, + targetProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrCount, + targetProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMask, + QHostAddress(targetProtoAddrMask->text()).toIPv4Address()); +} + +/* + * ------------ Private Slots -------------- + */ + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + diff --git a/common/arpconfig.h b/common/arpconfig.h new file mode 100644 index 0000000..15aa4bc --- /dev/null +++ b/common/arpconfig.h @@ -0,0 +1,46 @@ +/* +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 +*/ + +#ifndef _ARP_CONFIG_H +#define _ARP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_arp.h" + +class ArpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); + virtual ~ArpConfigForm(); + + static ArpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/arppdml.cpp b/common/arppdml.cpp new file mode 100644 index 0000000..9580db7 --- /dev/null +++ b/common/arppdml.cpp @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 "arppdml.h" + +#include "arp.pb.h" + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + diff --git a/common/arppdml.h b/common/arppdml.h new file mode 100644 index 0000000..039ed9b --- /dev/null +++ b/common/arppdml.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ARP_PDML_H +#define _ARP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +#endif diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..97fe960 --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,190 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + virtual int protocolFrameVariableCount() const + { + return AbstractProtocol::lcm( + protoA->protocolFrameVariableCount(), + protoB->protocolFrameVariableCount()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + template friend class ComboProtocolConfigForm; +}; + +#endif diff --git a/common/comboprotocolconfig.h b/common/comboprotocolconfig.h new file mode 100644 index 0000000..d77627f --- /dev/null +++ b/common/comboprotocolconfig.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_CONFIG_H +#define _COMBO_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "comboprotocol.h" + +template +class ComboProtocolConfigForm : + public AbstractProtocolConfigForm +{ +public: + ComboProtocolConfigForm(QWidget *parent = 0) + : AbstractProtocolConfigForm(parent) + { + QVBoxLayout *layout = new QVBoxLayout; + + formA = new FormA(this); + formB = new FormB(this); + + layout->addWidget(formA); + layout->addWidget(formB); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, formA, formB); + } + + virtual ~ComboProtocolConfigForm() + { + formA->setParent(0); + formB->setParent(0); + + delete formA; + delete formB; + } + + static ComboProtocolConfigForm* createInstance() + { + return new ComboProtocolConfigForm; + } + + virtual void loadWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->loadWidget(comboProto->protoA); + formB->loadWidget(comboProto->protoB); + } + virtual void storeWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->storeWidget(comboProto->protoA); + formB->storeWidget(comboProto->protoB); + } + +protected: + FormA *formA; + FormB *formB; +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2llcconfig.h b/common/dot2llcconfig.h new file mode 100644 index 0000000..76a5b24 --- /dev/null +++ b/common/dot2llcconfig.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _DOT2_LLC_CONFIG_H +#define _DOT2_LLC_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "dot3config.h" +#include "llcconfig.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2LlcFieldNumber, + Dot3ConfigForm, LlcConfigForm, + Dot3Protocol, LlcProtocol + > Dot2LlcConfigForm; + +#endif diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot2snapconfig.h b/common/dot2snapconfig.h new file mode 100644 index 0000000..32a8d99 --- /dev/null +++ b/common/dot2snapconfig.h @@ -0,0 +1,36 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_CONFIG_H +#define _DOT2_SNAP_CONFIG_H + +#include "comboprotocol.h" + +#include "dot2llcconfig.h" +#include "snapconfig.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2SnapFieldNumber, + Dot2LlcConfigForm, SnapConfigForm, + Dot2LlcProtocol, SnapProtocol + > Dot2SnapConfigForm; + +#endif diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..68ef9a6 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,196 @@ +/* +Copyright (C) 2010-2014 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 "dot3.h" + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Dot3Protocol::~Dot3Protocol() +{ +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + // Meta fields + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +int Dot3Protocol::protocolFrameVariableCount() const +{ + return protocolFramePayloadVariableCount(); +} diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..ecade93 --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" +#include "dot3.pb.h" + +class Dot3Protocol : public AbstractProtocol +{ +public: + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Dot3 data; +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/dot3config.cpp b/common/dot3config.cpp new file mode 100644 index 0000000..d17094e --- /dev/null +++ b/common/dot3config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010-2014 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 "dot3config.h" +#include "dot3.h" +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3ConfigForm::~Dot3ConfigForm() +{ +} + +Dot3ConfigForm* Dot3ConfigForm::createInstance() +{ + return new Dot3ConfigForm; +} + +void Dot3ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideLength->setChecked( + proto->fieldData( + Dot3Protocol::dot3_is_override_length, + AbstractProtocol::FieldValue + ).toBool()); + leLength->setText( + proto->fieldData( + Dot3Protocol::dot3_length, + AbstractProtocol::FieldValue + ).toString()); +} + +void Dot3ConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + Dot3Protocol::dot3_is_override_length, + cbOverrideLength->isChecked()); + proto->setFieldData( + Dot3Protocol::dot3_length, + leLength->text()); +} + diff --git a/common/dot3config.h b/common/dot3config.h new file mode 100644 index 0000000..44e3e7e --- /dev/null +++ b/common/dot3config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _DOT3_CONFIG_H +#define _DOT3_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : + public AbstractProtocolConfigForm, + private Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); + virtual ~Dot3ConfigForm(); + + static Dot3ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..63f361a --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,188 @@ +/* +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 +*/ + +#include "eth2.h" + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Eth2Protocol::~Eth2Protocol() +{ +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5846d14 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" + + +class Eth2Protocol : public AbstractProtocol +{ +public: + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); +private: + OstProto::Eth2 data; +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/eth2config.cpp b/common/eth2config.cpp new file mode 100644 index 0000000..7cf3bd5 --- /dev/null +++ b/common/eth2config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010,2014 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 "eth2config.h" +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +Eth2ConfigForm::~Eth2ConfigForm() +{ +} + +Eth2ConfigForm* Eth2ConfigForm::createInstance() +{ + return new Eth2ConfigForm; +} + +void Eth2ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideType->setChecked( + proto->fieldData( + Eth2Protocol::eth2_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + Eth2Protocol::eth2_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void Eth2ConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + Eth2Protocol::eth2_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + Eth2Protocol::eth2_type, + leType->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/eth2config.h b/common/eth2config.h new file mode 100644 index 0000000..918a855 --- /dev/null +++ b/common/eth2config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010,2014 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 +*/ + +#ifndef _ETH2_CONFIG_H +#define _ETH2_CONFIG_H + +#include "abstractprotocolconfig.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : + public AbstractProtocolConfigForm, + public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); + virtual ~Eth2ConfigForm(); + + static Eth2ConfigForm* createInstance(); + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; +#endif diff --git a/common/eth2pdml.cpp b/common/eth2pdml.cpp new file mode 100644 index 0000000..8bb8d17 --- /dev/null +++ b/common/eth2pdml.cpp @@ -0,0 +1,124 @@ +/* +Copyright (C) 2011 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 "eth2pdml.h" + +#include "dot3.pb.h" +#include "eth2.pb.h" +#include "mac.pb.h" +#include "vlan.pb.h" + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + diff --git a/common/eth2pdml.h b/common/eth2pdml.h new file mode 100644 index 0000000..b776147 --- /dev/null +++ b/common/eth2pdml.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ETH2_PDML_H +#define _ETH2_PDML_H + +#include "pdmlprotocol.h" + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +#endif diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..4edd980 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,483 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + postParseFixup(meta.data(), content); + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void FileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..409b8a8 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include "fileformat.pb.h" + +class FileFormat : public AbstractFileFormat +{ +public: + FileFormat(); + ~FileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 2; + static const uint kFileFormatVersionRevision = 3; +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..8d7b0fb --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,769 @@ +/* +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 +*/ + +#include "gmp.h" +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +GmpProtocol::~GmpProtocol() +{ +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +int GmpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + // No fields vary for Ssm Query and Report + if (isSsmReport() || isSsmQuery()) + return count; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(kGroupCount, FieldValue).toUInt()); + } + + return count; +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..a293c1f --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,129 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "abstractprotocol.h" +#include "gmp.pb.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ + +class GmpProtocol : public AbstractProtocol +{ +public: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +protected: + OstProto::Gmp data; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; + +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
    diff --git a/common/gmpconfig.cpp b/common/gmpconfig.cpp new file mode 100644 index 0000000..ee6af11 --- /dev/null +++ b/common/gmpconfig.cpp @@ -0,0 +1,384 @@ +/* +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 +*/ + +#include "gmpconfig.h" +#include "gmp.h" + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + msgTypeCombo->setValue( + proto->fieldData( + GmpProtocol::kType, + AbstractProtocol::FieldValue + ).toUInt()); + + // XXX: maxResponseTime set by subclass + + overrideChecksum->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideChecksum, + AbstractProtocol::FieldValue + ).toBool()); + checksum->setText(uintToHexStr( + proto->fieldData( + GmpProtocol::kChecksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + groupAddress->setText( + proto->fieldData( + GmpProtocol::kGroupAddress, + AbstractProtocol::FieldValue + ).toString()); + groupMode->setCurrentIndex( + proto->fieldData( + GmpProtocol::kGroupMode, + AbstractProtocol::FieldValue + ).toUInt()); + groupCount->setText( + proto->fieldData( + GmpProtocol::kGroupCount, + AbstractProtocol::FieldValue + ).toString()); + groupPrefix->setText( + proto->fieldData( + GmpProtocol::kGroupPrefix, + AbstractProtocol::FieldValue + ).toString()); + + sFlag->setChecked( + proto->fieldData( + GmpProtocol::kSFlag, + AbstractProtocol::FieldValue + ).toBool()); + qrv->setText( + proto->fieldData( + GmpProtocol::kQrv, + AbstractProtocol::FieldValue + ).toString()); + qqi->setText( + proto->fieldData( + GmpProtocol::kQqic, + AbstractProtocol::FieldValue + ).toString()); + + QStringList sl = + proto->fieldData( + GmpProtocol::kSources, + AbstractProtocol::FieldValue + ).toStringList(); + sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + overrideSourceCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideSourceCount, + AbstractProtocol::FieldValue + ).toBool()); + sourceCount->setText( + proto->fieldData( + GmpProtocol::kSourceCount, + AbstractProtocol::FieldValue + ).toString()); + + QVariantList list = + proto->fieldData( + GmpProtocol::kGroupRecords, + AbstractProtocol::FieldValue + ).toList(); + groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + overrideGroupRecordCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + AbstractProtocol::FieldValue + ).toBool()); + groupRecordCount->setText( + proto->fieldData( + GmpProtocol::kGroupRecordCount, + AbstractProtocol::FieldValue + ).toString()); +} + +void GmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + update(); + + proto->setFieldData( + GmpProtocol::kType, + msgTypeCombo->currentValue()); + + // XXX: maxResponseTime handled by subclass + + proto->setFieldData( + GmpProtocol::kIsOverrideChecksum, + overrideChecksum->isChecked()); + proto->setFieldData( + GmpProtocol::kChecksum, + hexStrToUInt(checksum->text())); + + proto->setFieldData( + GmpProtocol::kGroupAddress, + groupAddress->text()); + proto->setFieldData( + GmpProtocol::kGroupMode, + groupMode->currentIndex()); + proto->setFieldData( + GmpProtocol::kGroupCount, + groupCount->text()); + proto->setFieldData( + GmpProtocol::kGroupPrefix, + groupPrefix->text().remove('/')); + + proto->setFieldData( + GmpProtocol::kSFlag, + sFlag->isChecked()); + proto->setFieldData( + GmpProtocol::kQrv, + qrv->text()); + proto->setFieldData( + GmpProtocol::kQqic, + qqi->text()); + + QStringList list; + for (int i = 0; i < sourceList->count(); i++) + list.append(sourceList->item(i)->text()); + + proto->setFieldData( + GmpProtocol::kSources, + list); + + // sourceCount should be AFTER sources + proto->setFieldData( + GmpProtocol::kIsOverrideSourceCount, + overrideSourceCount->isChecked()); + proto->setFieldData( + GmpProtocol::kSourceCount, + sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < groupList->count(); i++) + { + QVariant grp = groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + proto->setFieldData(GmpProtocol::kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + proto->setFieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + overrideGroupRecordCount->isChecked()); + proto->setFieldData( + GmpProtocol::kGroupRecordCount, + groupRecordCount->text()); +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +// +// -- private slots +// + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} diff --git a/common/gmpconfig.h b/common/gmpconfig.h new file mode 100644 index 0000000..70a833b --- /dev/null +++ b/common/gmpconfig.h @@ -0,0 +1,63 @@ +/* +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 +*/ + +#ifndef _GMP_CONFIG_H +#define _GMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_gmp.h" + +class GmpConfigForm : + public AbstractProtocolConfigForm, + protected Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; + +private: + void update(); + +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +#endif diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..376ccf0 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,211 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +HexDumpProtocol::~HexDumpProtocol() +{ +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..4318192 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,73 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "abstractprotocol.h" +#include "hexdump.pb.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpProtocol : public AbstractProtocol +{ +public: + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + +private: + OstProto::HexDump data; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
    qhexedit.h
    + 1 +
    +
    + + +
    diff --git a/common/hexdumpconfig.cpp b/common/hexdumpconfig.cpp new file mode 100644 index 0000000..1c057b6 --- /dev/null +++ b/common/hexdumpconfig.cpp @@ -0,0 +1,75 @@ +/* +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 +*/ + +#include "hexdumpconfig.h" +#include "hexdump.h" + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +HexDumpConfigForm::~HexDumpConfigForm() +{ +} + +HexDumpConfigForm* HexDumpConfigForm::createInstance() +{ + return new HexDumpConfigForm; +} + +void HexDumpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hexEdit->setData( + proto->fieldData( + HexDumpProtocol::hexDump_content, + AbstractProtocol::FieldValue + ).toByteArray()); + padUntilEnd->setChecked( + proto->fieldData( + HexDumpProtocol::hexDump_pad_until_end, + AbstractProtocol::FieldValue + ).toBool()); +} + +void HexDumpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + HexDumpProtocol::hexDump_content, + hexEdit->data()); + proto->setFieldData( + HexDumpProtocol::hexDump_pad_until_end, + padUntilEnd->isChecked()); +} + +// +// ------------ private slots +// +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + diff --git a/common/hexdumpconfig.h b/common/hexdumpconfig.h new file mode 100644 index 0000000..b0dcfa7 --- /dev/null +++ b/common/hexdumpconfig.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _HEX_DUMP_CONFIG_H +#define _HEX_DUMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_hexdump.h" + +class HexDumpConfigForm : + public AbstractProtocolConfigForm, + private Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); + virtual ~HexDumpConfigForm(); + + static HexDumpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +#endif diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..651efb5 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,396 @@ +/* +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 +*/ + +#include "icmp.h" +#include "icmphelper.h" + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +IcmpProtocol::~IcmpProtocol() +{ +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + + + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..b24f025 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,96 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "abstractprotocol.h" +#include "icmp.pb.h" + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpProtocol : public AbstractProtocol +{ +public: + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Icmp data; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
    diff --git a/common/icmp6pdml.cpp b/common/icmp6pdml.cpp new file mode 100644 index 0000000..53c4a34 --- /dev/null +++ b/common/icmp6pdml.cpp @@ -0,0 +1,100 @@ +/* +Copyright (C) 2011 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 "icmp6pdml.h" + +#include "icmp.pb.h" +#include "sample.pb.h" + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + diff --git a/common/icmp6pdml.h b/common/icmp6pdml.h new file mode 100644 index 0000000..d159824 --- /dev/null +++ b/common/icmp6pdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP6_PDML_H +#define _ICMP6_PDML_H + +#include "pdmlprotocol.h" + +#include "icmppdml.h" +#include "mldpdml.h" + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +#endif diff --git a/common/icmpconfig.cpp b/common/icmpconfig.cpp new file mode 100644 index 0000000..2bf65af --- /dev/null +++ b/common/icmpconfig.cpp @@ -0,0 +1,191 @@ +/* +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 +*/ + +#include "icmpconfig.h" + +#include "icmp.h" +#include "icmphelper.h" + +#include + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +IcmpConfigForm::~IcmpConfigForm() +{ +} + +IcmpConfigForm* IcmpConfigForm::createInstance() +{ + return new IcmpConfigForm; +} + +void IcmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + versionGroup->button( + proto->fieldData( + IcmpProtocol::icmp_version, + AbstractProtocol::FieldValue + ).toUInt())->click(); + + typeCombo->setValue( + proto->fieldData( + IcmpProtocol::icmp_type, + AbstractProtocol::FieldValue + ).toUInt()); + codeEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_code, + AbstractProtocol::FieldValue + ).toString()); + + overrideCksum->setChecked( + proto->fieldData( + IcmpProtocol::icmp_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + cksumEdit->setText(uintToHexStr( + proto->fieldData( + IcmpProtocol::icmp_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + idEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_identifier, + AbstractProtocol::FieldValue + ).toString()); + seqEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_sequence, + AbstractProtocol::FieldValue + ).toString()); +} + +void IcmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + IcmpProtocol::icmp_version, + versionGroup->checkedId()); + + proto->setFieldData( + IcmpProtocol::icmp_type, + typeCombo->currentValue()); + proto->setFieldData( + IcmpProtocol::icmp_code, + codeEdit->text()); + + proto->setFieldData( + IcmpProtocol::icmp_is_override_checksum, + overrideCksum->isChecked()); + proto->setFieldData( + IcmpProtocol::icmp_checksum, + hexStrToUInt(cksumEdit->text())); + + proto->setFieldData( + IcmpProtocol::icmp_identifier, + idEdit->text()); + proto->setFieldData( + IcmpProtocol::icmp_sequence, + seqEdit->text()); +} + +// +// -------- private slots +// +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + diff --git a/common/icmpconfig.h b/common/icmpconfig.h new file mode 100644 index 0000000..6e01065 --- /dev/null +++ b/common/icmpconfig.h @@ -0,0 +1,50 @@ +/* +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 +*/ + +#ifndef _ICMP_CONFIG_H +#define _ICMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_icmp.h" + +class QButtonGroup; + +class IcmpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Icmp +{ + Q_OBJECT +public: + IcmpConfigForm(QWidget *parent = 0); + virtual ~IcmpConfigForm(); + + static IcmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + QButtonGroup *versionGroup; + +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +#endif diff --git a/common/icmphelper.h b/common/icmphelper.h new file mode 100644 index 0000000..0c5bcf7 --- /dev/null +++ b/common/icmphelper.h @@ -0,0 +1,88 @@ +/* +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 +*/ + +#ifndef _ICMP_HELPER_H +#define _ICMP_HELPER_H + +#include "icmp.pb.h" + +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +bool inline isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +#endif diff --git a/common/icmppdml.cpp b/common/icmppdml.cpp new file mode 100644 index 0000000..0bff798 --- /dev/null +++ b/common/icmppdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "icmppdml.h" + +#include "icmp.pb.h" + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/icmppdml.h b/common/icmppdml.h new file mode 100644 index 0000000..58b3e37 --- /dev/null +++ b/common/icmppdml.h @@ -0,0 +1,48 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP_PDML_H +#define _ICMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +#endif diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..8a5a0b9 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,364 @@ +/* +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 +*/ + +#include "igmp.h" +#include "iputils.h" + +#include +#include + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..0634a1f --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,98 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "gmp.h" +#include "igmp.pb.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/igmpconfig.cpp b/common/igmpconfig.cpp new file mode 100644 index 0000000..d7bdaa4 --- /dev/null +++ b/common/igmpconfig.cpp @@ -0,0 +1,112 @@ +/* +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 +*/ + +#include "igmpconfig.h" +#include "igmp.h" +#include "ipv4addressdelegate.h" + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +IgmpConfigForm::~IgmpConfigForm() +{ +} + +IgmpConfigForm* IgmpConfigForm::createInstance() +{ + return new IgmpConfigForm; +} + +void IgmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + IgmpProtocol::kRsvdMrtCode, + AbstractProtocol::FieldValue + ).toString()); +} + +void IgmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + IgmpProtocol::kRsvdMrtCode, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + diff --git a/common/igmpconfig.h b/common/igmpconfig.h new file mode 100644 index 0000000..6428db1 --- /dev/null +++ b/common/igmpconfig.h @@ -0,0 +1,40 @@ +/* +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 +*/ +#ifndef _IGMP_CONFIG_H +#define _IGMP_CONFIG_H + +#include "gmpconfig.h" + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); + virtual ~IgmpConfigForm(); + + static IgmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/igmppdml.cpp b/common/igmppdml.cpp new file mode 100644 index 0000000..19516d7 --- /dev/null +++ b/common/igmppdml.cpp @@ -0,0 +1,141 @@ +/* +Copyright (C) 2011 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 "igmppdml.h" + +#include "igmp.pb.h" + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/igmppdml.h b/common/igmppdml.h new file mode 100644 index 0000000..4b553a7 --- /dev/null +++ b/common/igmppdml.h @@ -0,0 +1,49 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IGMP_PDML_H +#define _IGMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +#endif diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..1c1557b --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,846 @@ +/* +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 +*/ + +#include "ip4.h" + +#include + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Ip4Protocol::~Ip4Protocol() +{ +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + + // Meta fields + case ip4_isOverrideVer: + switch(attrib) + { + case FieldValue: return data.is_override_ver(); + default: break; + } + break; + case ip4_isOverrideHdrLen: + switch(attrib) + { + case FieldValue: return data.is_override_hdrlen(); + default: break; + } + break; + case ip4_isOverrideTotLen: + switch(attrib) + { + case FieldValue: return data.is_override_totlen(); + default: break; + } + break; + case ip4_isOverrideProto: + switch(attrib) + { + case FieldValue: return data.is_override_proto(); + default: break; + } + break; + case ip4_isOverrideCksum: + switch(attrib) + { + case FieldValue: return data.is_override_cksum(); + default: break; + } + break; + + case ip4_srcAddrMode: + switch(attrib) + { + case FieldValue: return data.src_ip_mode(); + default: break; + } + break; + case ip4_srcAddrCount: + switch(attrib) + { + case FieldValue: return data.src_ip_count(); + default: break; + } + break; + case ip4_srcAddrMask: + switch(attrib) + { + case FieldValue: return data.src_ip_mask(); + default: break; + } + break; + + case ip4_dstAddrMode: + switch(attrib) + { + case FieldValue: return data.dst_ip_mode(); + default: break; + } + break; + case ip4_dstAddrCount: + switch(attrib) + { + case FieldValue: return data.dst_ip_count(); + default: break; + } + break; + case ip4_dstAddrMask: + switch(attrib) + { + case FieldValue: return data.dst_ip_mask(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case ip4_ver: + { + uint version = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + ((version & 0xF) << 4) + | (data.ver_hdrlen() & 0x0F)); + break; + } + case ip4_hdrLen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + (data.ver_hdrlen() & 0xF0) + | (hdrLen & 0x0F)); + break; + } + case ip4_tos: + { + uint tos = value.toUInt(&isOk); + if (isOk) + data.set_tos(tos); + break; + } + case ip4_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case ip4_id: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_id(id); + break; + } + case ip4_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case ip4_fragOfs: + { + uint fragOfs = value.toUInt(&isOk); + if (isOk) + data.set_frag_ofs(fragOfs); + break; + } + case ip4_ttl: + { + uint ttl = value.toUInt(&isOk); + if (isOk) + data.set_ttl(ttl); + break; + } + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + break; + } + case ip4_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case ip4_srcAddr: + { + quint32 srcIp = value.toUInt(&isOk); + if (isOk) + data.set_src_ip(srcIp); + break; + } + case ip4_dstAddr: + { + quint32 dstIp = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip(dstIp); + break; + } + + // Meta-fields + case ip4_isOverrideVer: + { + bool ovr = value.toBool(); + data.set_is_override_ver(ovr); + isOk = true; + break; + } + case ip4_isOverrideHdrLen: + { + bool ovr = value.toBool(); + data.set_is_override_hdrlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideTotLen: + { + bool ovr = value.toBool(); + data.set_is_override_totlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideProto: + { + bool ovr = value.toBool(); + data.set_is_override_proto(ovr); + isOk = true; + break; + } + case ip4_isOverrideCksum: + { + bool ovr = value.toBool(); + data.set_is_override_cksum(ovr); + isOk = true; + break; + } + + case ip4_srcAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_src_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_srcAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_count(count); + break; + } + case ip4_srcAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_mask(mask); + break; + } + + case ip4_dstAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_dst_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_dstAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_count(count); + break; + } + case ip4_dstAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_mask(mask); + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +int Ip4Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.src_ip_count()); + + if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.dst_ip_count()); + + return count; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..1ea8128 --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,99 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" +#include "ip4.pb.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + +class Ip4Protocol : public AbstractProtocol +{ +public: + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + // Meta-fields + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + +private: + OstProto::Ip4 data; +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4config.cpp b/common/ip4config.cpp new file mode 100644 index 0000000..261da7a --- /dev/null +++ b/common/ip4config.cpp @@ -0,0 +1,297 @@ +/* +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 +*/ + +#include "ip4config.h" +#include "ip4.h" + +#include + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ +} + + +Ip4ConfigForm* Ip4ConfigForm::createInstance() +{ + return new Ip4ConfigForm; +} + +void Ip4ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbIpVersionOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideVer, + AbstractProtocol::FieldValue + ).toBool()); + leIpVersion->setText( + proto->fieldData( + Ip4Protocol::ip4_ver, + AbstractProtocol::FieldValue + ).toString()); + + cbIpHdrLenOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpHdrLen->setText( + proto->fieldData( + Ip4Protocol::ip4_hdrLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpTos->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_tos, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpLengthOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpLength->setText( + proto->fieldData( + Ip4Protocol::ip4_totLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpId->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_id, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + leIpFragOfs->setText( + proto->fieldData( + Ip4Protocol::ip4_fragOfs, + AbstractProtocol::FieldValue + ).toString()); + cbIpFlagsDf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_DF) > 0); + cbIpFlagsMf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_MF) > 0); + + leIpTtl->setText( + proto->fieldData( + Ip4Protocol::ip4_ttl, + AbstractProtocol::FieldValue + ).toString()); + + cbIpProtocolOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideProto, + AbstractProtocol::FieldValue + ).toBool()); + leIpProto->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_proto, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpCksumOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); + leIpCksum->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + leIpSrcAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpSrcAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpSrcAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpSrcAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + leIpDstAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpDstAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpDstAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpDstAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void Ip4ConfigForm::storeWidget(AbstractProtocol *proto) +{ + uint ff = 0; + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideVer, + cbIpVersionOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_ver, + leIpVersion->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + cbIpHdrLenOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_hdrLen, + leIpHdrLen->text()); + + proto->setFieldData( + Ip4Protocol::ip4_tos, + hexStrToUInt(leIpTos->text())); + + proto->setFieldData( + Ip4Protocol::ip4_totLen, + leIpLength->text()); + proto->setFieldData( + Ip4Protocol::ip4_isOverrideTotLen, + cbIpLengthOverride->isChecked()); + + proto->setFieldData( + Ip4Protocol::ip4_id, + hexStrToUInt(leIpId->text())); + proto->setFieldData( + Ip4Protocol::ip4_fragOfs, + leIpFragOfs->text()); + + if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + proto->setFieldData( + Ip4Protocol::ip4_flags, + ff); + + proto->setFieldData( + Ip4Protocol::ip4_ttl, + leIpTtl->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideProto, + cbIpProtocolOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_proto, + hexStrToUInt(leIpProto->text())); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideCksum, + cbIpCksumOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_cksum, + hexStrToUInt(leIpCksum->text())); + + proto->setFieldData( + Ip4Protocol::ip4_srcAddr, + QHostAddress(leIpSrcAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpSrcAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrCount, + leIpSrcAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMask, + QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + Ip4Protocol::ip4_dstAddr, + QHostAddress(leIpDstAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpDstAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrCount, + leIpDstAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMask, + QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); +} + +/* + * Slots + */ +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} diff --git a/common/ip4config.h b/common/ip4config.h new file mode 100644 index 0000000..6db7b97 --- /dev/null +++ b/common/ip4config.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _IPV4_CONFIG_H +#define _IPV4_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip4.h" + +class Ip4ConfigForm : + public AbstractProtocolConfigForm, + private Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + virtual ~Ip4ConfigForm(); + + static Ip4ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; +#endif diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over4config.h b/common/ip4over4config.h new file mode 100644 index 0000000..328b14e --- /dev/null +++ b/common/ip4over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_CONFIG_H +#define _IP_4_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip4.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over4FieldNumber, + Ip4ConfigForm, Ip4ConfigForm, + Ip4Protocol, Ip4Protocol + > Ip4over4ConfigForm; + +#endif diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip4over6config.h b/common/ip4over6config.h new file mode 100644 index 0000000..b0ccf63 --- /dev/null +++ b/common/ip4over6config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_CONFIG_H +#define _IP_4_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over6FieldNumber, + Ip6ConfigForm, Ip4ConfigForm, + Ip6Protocol, Ip4Protocol + > Ip4over6ConfigForm; + +#endif diff --git a/common/ip4pdml.cpp b/common/ip4pdml.cpp new file mode 100644 index 0000000..f355260 --- /dev/null +++ b/common/ip4pdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "ip4pdml.h" + +#include "hexdump.pb.h" +#include "ip4.pb.h" + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/ip4pdml.h b/common/ip4pdml.h new file mode 100644 index 0000000..64f818d --- /dev/null +++ b/common/ip4pdml.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP4_PDML_H +#define _IP4_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +#endif diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..79a0868 --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,806 @@ +/* +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 +*/ + +#include "ip6.h" +#include + + +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); + 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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +int Ip6Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + 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) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..63f6e7e --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,112 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "abstractprotocol.h" +#include "ip6.pb.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6Protocol : public AbstractProtocol +{ +public: + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +private: + OstProto::Ip6 data; +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6config.cpp b/common/ip6config.cpp new file mode 100644 index 0000000..1ddd20b --- /dev/null +++ b/common/ip6config.cpp @@ -0,0 +1,233 @@ +/* +Copyright (C) 2010-2014 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 "ip6config.h" +#include "ip6.h" +#include "ipv6addressvalidator.h" +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +AbstractProtocolConfigForm* Ip6ConfigForm::createInstance() +{ + return new Ip6ConfigForm; +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::loadWidget(AbstractProtocol *ip6Proto) +{ + isVersionOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideVersion, + AbstractProtocol::FieldValue + ).toBool()); + version->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_version, + AbstractProtocol::FieldValue + ).toString()); + + trafficClass->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_trafficClass, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + flowLabel->setText(QString("%1").arg( + ip6Proto->fieldData( + Ip6Protocol::ip6_flowLabel, + AbstractProtocol::FieldValue + ).toUInt(), 5, BASE_HEX, QChar('0'))); + + isPayloadLengthOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + AbstractProtocol::FieldValue + ).toBool()); + payloadLength->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isNextHeaderOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + AbstractProtocol::FieldValue + ).toBool()); + nextHeader->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_nextHeader, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + hopLimit->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_hopLimit, + AbstractProtocol::FieldValue + ).toString()); + + srcAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddress, + AbstractProtocol::FieldTextValue + ).toString()); + srcAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + srcAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + srcAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); + + dstAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddress, + AbstractProtocol::FieldTextValue + ).toString()); + dstAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + dstAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + dstAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); +} + +void Ip6ConfigForm::storeWidget(AbstractProtocol *ip6Proto) +{ + bool isOk; + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideVersion, + isVersionOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_version, + version->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_trafficClass, + trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_flowLabel, + flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + isPayloadLengthOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_payloadLength, + payloadLength->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + isNextHeaderOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_nextHeader, + nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_hopLimit, + hopLimit->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddress, + srcAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrMode, + srcAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrCount, + srcAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrPrefix, + srcAddrPrefix->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddress, + dstAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrMode, + dstAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrCount, + dstAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrPrefix, + dstAddrPrefix->text()); +} + diff --git a/common/ip6config.h b/common/ip6config.h new file mode 100644 index 0000000..0ea08c1 --- /dev/null +++ b/common/ip6config.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ +#ifndef _IP6_CONFIG_H +#define _IP6_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip6.h" + +class Ip6ConfigForm : + public AbstractProtocolConfigForm, + private Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *ip6Proto); + virtual void storeWidget(AbstractProtocol *ip6Proto); + +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over4config.h b/common/ip6over4config.h new file mode 100644 index 0000000..b3c2390 --- /dev/null +++ b/common/ip6over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_CONFIG_H +#define _IP_6_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over4FieldNumber, + Ip4ConfigForm, Ip6ConfigForm, + Ip4Protocol, Ip6Protocol + > Ip6over4ConfigForm; + +#endif diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/ip6over6config.h b/common/ip6over6config.h new file mode 100644 index 0000000..4324fe7 --- /dev/null +++ b/common/ip6over6config.h @@ -0,0 +1,33 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_CONFIG_H +#define _IP_6_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip6config.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over6FieldNumber, + Ip6ConfigForm, Ip6ConfigForm, + Ip6Protocol, Ip6Protocol + > Ip6over6ConfigForm; + +#endif diff --git a/common/ip6pdml.cpp b/common/ip6pdml.cpp new file mode 100644 index 0000000..2f3a7f8 --- /dev/null +++ b/common/ip6pdml.cpp @@ -0,0 +1,76 @@ +/* +Copyright (C) 2011 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 "ip6pdml.h" + +#include "ip6.pb.h" + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + diff --git a/common/ip6pdml.h b/common/ip6pdml.h new file mode 100644 index 0000000..4f766b4 --- /dev/null +++ b/common/ip6pdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP6_PDML_H +#define _IP6_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +#endif diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..67b370d --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,264 @@ +/* +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 +*/ + +#include "llc.h" + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +LlcProtocol::~LlcProtocol() +{ +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..a723177 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,71 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include "abstractprotocol.h" + +#include "llc.pb.h" + +class LlcProtocol : public AbstractProtocol +{ +public: + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Llc data; +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/llcconfig.cpp b/common/llcconfig.cpp new file mode 100644 index 0000000..6ba785c --- /dev/null +++ b/common/llcconfig.cpp @@ -0,0 +1,100 @@ +/* +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 +*/ + +#include "llcconfig.h" +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +LlcConfigForm::~LlcConfigForm() +{ +} + +LlcConfigForm* LlcConfigForm::createInstance() +{ + return new LlcConfigForm; +} + +void LlcConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideDsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_dsap, + AbstractProtocol::FieldValue + ).toBool()); + leDsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_dsap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideSsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ssap, + AbstractProtocol::FieldValue + ).toBool()); + leSsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ssap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideControl->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ctl, + AbstractProtocol::FieldValue + ).toBool()); + leControl->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ctl, + AbstractProtocol::FieldValue + ).toUInt(), 1)); +} + +void +LlcConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + LlcProtocol::llc_is_override_dsap, + cbOverrideDsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_dsap, + leDsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ssap, + cbOverrideSsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ssap, + leSsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ctl, + cbOverrideControl->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ctl, + leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llcconfig.h b/common/llcconfig.h new file mode 100644 index 0000000..08bd3f4 --- /dev/null +++ b/common/llcconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _LLC_CONFIG_H +#define _LLC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_llc.h" + +class LlcConfigForm : + public AbstractProtocolConfigForm, + private Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); + virtual ~LlcConfigForm(); + + static LlcConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/llcpdml.cpp b/common/llcpdml.cpp new file mode 100644 index 0000000..a5584cc --- /dev/null +++ b/common/llcpdml.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2011 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 "llcpdml.h" + +#include "llc.pb.h" +#include "snap.pb.h" + +#include + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + diff --git a/common/llcpdml.h b/common/llcpdml.h new file mode 100644 index 0000000..ace0dcb --- /dev/null +++ b/common/llcpdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _LLC_PDML_H +#define _LLC_PDML_H + +#include "pdmlprotocol.h" + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +#endif diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..70d047b --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,351 @@ +/* +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 +*/ + +#include "mac.h" + +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +MacProtocol::~MacProtocol() +{ +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToMacStr(dstMac); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToMacStr(srcMac); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case mac_dstMacMode: + switch(attrib) + { + case FieldValue: return data.dst_mac_mode(); + default: break; + } + break; + case mac_dstMacCount: + switch(attrib) + { + case FieldValue: return data.dst_mac_count(); + default: break; + } + break; + case mac_dstMacStep: + switch(attrib) + { + case FieldValue: return data.dst_mac_step(); + default: break; + } + break; + case mac_srcMacMode: + switch(attrib) + { + case FieldValue: return data.src_mac_mode(); + default: break; + } + break; + case mac_srcMacCount: + switch(attrib) + { + case FieldValue: return data.src_mac_count(); + default: break; + } + break; + case mac_srcMacStep: + switch(attrib) + { + case FieldValue: return data.src_mac_step(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case mac_dstAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_dst_mac(mac); + break; + } + case mac_srcAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_src_mac(mac); + break; + } + + // Meta-Fields + case mac_dstMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_dstMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_count(count); + break; + } + case mac_dstMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_step(step); + break; + } + case mac_srcMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_srcMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_count(count); + break; + } + case mac_srcMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_step(step); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + +int MacProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.dst_mac_count()); + + if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.src_mac_count()); + + return count; +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..85e0ad5 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,73 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" + +class MacProtocol : public AbstractProtocol +{ +public: + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Mac data; +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/macconfig.cpp b/common/macconfig.cpp new file mode 100644 index 0000000..f17c140 --- /dev/null +++ b/common/macconfig.cpp @@ -0,0 +1,148 @@ +/* +Copyright (C) 2010,2013-2014 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 "macconfig.h" +#include "mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +MacConfigForm::MacConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ +} + +MacConfigForm* MacConfigForm::createInstance() +{ + MacConfigForm *f = new MacConfigForm; + return f; +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + +void MacConfigForm::loadWidget(AbstractProtocol *proto) +{ + leDstMac->setText( + proto->fieldData( + MacProtocol::mac_dstAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbDstMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_dstMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leDstMacCount->setText( + proto->fieldData( + MacProtocol::mac_dstMacCount, + AbstractProtocol::FieldValue + ).toString()); + leDstMacStep->setText( + proto->fieldData( + MacProtocol::mac_dstMacStep, + AbstractProtocol::FieldValue + ).toString()); + + leSrcMac->setText( + proto->fieldData( + MacProtocol::mac_srcAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbSrcMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_srcMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leSrcMacCount->setText( + proto->fieldData( + MacProtocol::mac_srcMacCount, + AbstractProtocol::FieldValue + ).toString()); + leSrcMacStep->setText( + proto->fieldData( + MacProtocol::mac_srcMacStep, + AbstractProtocol::FieldValue + ).toString()); +} + +void MacConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + MacProtocol::mac_dstAddr, + leDstMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_dstMacMode, + cmbDstMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_dstMacCount, + leDstMacCount->text()); + proto->setFieldData( + MacProtocol::mac_dstMacStep, + leDstMacStep->text()); + + proto->setFieldData( + MacProtocol::mac_srcAddr, + leSrcMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_srcMacMode, + cmbSrcMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_srcMacCount, + leSrcMacCount->text()); + proto->setFieldData( + MacProtocol::mac_srcMacStep, + leSrcMacStep->text()); +} + diff --git a/common/macconfig.h b/common/macconfig.h new file mode 100644 index 0000000..952c64e --- /dev/null +++ b/common/macconfig.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _MAC_CONFIG_H +#define _MAC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_mac.h" + +class MacConfigForm : + public AbstractProtocolConfigForm, + private Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); + + static MacConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..52b48a5 --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,543 @@ +/* +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 +*/ + +#include "mld.h" + +#include "iputils.h" + +#include +#include + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j+8)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..5403af6 --- /dev/null +++ b/common/mld.h @@ -0,0 +1,95 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "gmp.h" +#include "mld.pb.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/mldconfig.cpp b/common/mldconfig.cpp new file mode 100644 index 0000000..d6b7b4b --- /dev/null +++ b/common/mldconfig.cpp @@ -0,0 +1,109 @@ +/* +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 +*/ + +#include "mldconfig.h" +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +MldConfigForm::~MldConfigForm() +{ +} + +MldConfigForm* MldConfigForm::createInstance() +{ + return new MldConfigForm; +} + +void MldConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + MldProtocol::kMldMrt, + AbstractProtocol::FieldValue + ).toString()); +} + +void MldConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + MldProtocol::kMldMrt, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} diff --git a/common/mldconfig.h b/common/mldconfig.h new file mode 100644 index 0000000..b25617a --- /dev/null +++ b/common/mldconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ +#ifndef _MLD_CONFIG_H +#define _MLD_CONFIG_H + +#include "gmpconfig.h" + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); + + virtual ~MldConfigForm(); + + static MldConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/mldpdml.cpp b/common/mldpdml.cpp new file mode 100644 index 0000000..b17503e --- /dev/null +++ b/common/mldpdml.cpp @@ -0,0 +1,133 @@ +/* +Copyright (C) 2011 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 "mldpdml.h" + +#include "mld.pb.h" + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + diff --git a/common/mldpdml.h b/common/mldpdml.h new file mode 100644 index 0000000..0c47fbe --- /dev/null +++ b/common/mldpdml.h @@ -0,0 +1,47 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _MLD_PDML_H +#define _MLD_PDML_H + +#include "pdmlprotocol.h" + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..05024e4 --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,111 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT -= gui +QT += network script +LIBS += \ + -lprotobuf + +PROTOS = \ + protocol.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto + +HEADERS = \ + abstractprotocol.h \ + comboprotocol.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + +HEADERS += \ + mac.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + eth2.h \ + dot3.h \ + llc.h \ + dot2llc.h \ + snap.h \ + dot2snap.h \ + arp.h \ + ip4.h \ + ip6.h \ + ip4over4.h \ + ip4over6.h \ + ip6over4.h \ + ip6over6.h \ + gmp.h \ + icmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + hexdump.h \ + payload.h \ + sample.h \ + userscript.h + +SOURCES = \ + abstractprotocol.cpp \ + crc32c.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + +SOURCES += \ + mac.cpp \ + vlan.cpp \ + svlan.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + gmp.cpp \ + icmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + hexdump.cpp \ + payload.cpp \ + sample.cpp \ + userscript.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) + diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro new file mode 100644 index 0000000..b075046 --- /dev/null +++ b/common/ostprotogui.pro @@ -0,0 +1,129 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network xml script +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf + +FORMS = \ + pcapfileimport.ui \ + +FORMS += \ + mac.ui \ + vlan.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + gmp.ui \ + icmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + hexdump.ui \ + payload.ui \ + sample.ui \ + userscript.ui + +PROTOS = \ + fileformat.proto + +# TODO: Move fileformat related stuff into a different library - why? +HEADERS = \ + ostprotolib.h \ + abstractfileformat.h \ + fileformat.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h + +HEADERS += \ + abstractprotocolconfig.h \ + comboprotocolconfig.h \ + protocolwidgetfactory.h \ + macconfig.h \ + vlanconfig.h \ + svlanconfig.h \ + vlanstackconfig.h \ + eth2config.h \ + dot3config.h \ + llcconfig.h \ + dot2llcconfig.h \ + snapconfig.h \ + dot2snapconfig.h \ + arpconfig.h \ + ip4config.h \ + ip6config.h \ + ip4over4config.h \ + gmpconfig.h \ + icmpconfig.h \ + igmpconfig.h \ + mldconfig.h \ + tcpconfig.h \ + udpconfig.h \ + textprotoconfig.h \ + hexdumpconfig.h \ + payloadconfig.h \ + sampleconfig.h \ + userscriptconfig.h + +SOURCES += \ + ostprotolib.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + +SOURCES += \ + protocolwidgetfactory.cpp \ + macconfig.cpp \ + vlanconfig.cpp \ + eth2config.cpp \ + dot3config.cpp \ + llcconfig.cpp \ + snapconfig.cpp \ + arpconfig.cpp \ + ip4config.cpp \ + ip6config.cpp \ + gmpconfig.cpp \ + icmpconfig.cpp \ + igmpconfig.cpp \ + mldconfig.cpp \ + tcpconfig.cpp \ + udpconfig.cpp \ + textprotoconfig.cpp \ + hexdumpconfig.cpp \ + payloadconfig.cpp \ + sampleconfig.cpp \ + userscriptconfig.cpp + +SOURCES += \ + vlanpdml.cpp \ + svlanpdml.cpp \ + eth2pdml.cpp \ + llcpdml.cpp \ + arppdml.cpp \ + ip4pdml.cpp \ + ip6pdml.cpp \ + icmppdml.cpp \ + icmp6pdml.cpp \ + igmppdml.cpp \ + mldpdml.cpp \ + tcppdml.cpp \ + udppdml.cpp \ + textprotopdml.cpp \ + samplepdml.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..bd1fcc2 --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +// TODO: one set method for each external app +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..2a7756d --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,258 @@ +/* +Copyright (C) 2010, 2013-2014 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 "payload.h" +#include "streambase.h" + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +PayloadProtocol::~PayloadProtocol() +{ +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + switch(attrib) + { + case FieldValue: return data.pattern_mode(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case payload_dataPattern: + { + uint pattern = value.toUInt(&isOk); + if (isOk) + data.set_pattern(pattern); + break; + } + case payload_dataPatternMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.DataPatternMode_IsValid(mode)) + data.set_pattern_mode(OstProto::Payload::DataPatternMode(mode)); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + +int PayloadProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.pattern_mode() == OstProto::Payload::e_dp_random) + { + switch(mpStream->sendUnit()) + { + case OstProto::StreamControl::e_su_packets: + return mpStream->numPackets(); + + case OstProto::StreamControl::e_su_bursts: + return int(mpStream->numBursts() + * mpStream->burstSize() + * mpStream->burstRate()); + } + } + + if (mpStream->lenMode() != StreamBase::e_fl_fixed) + { + count = AbstractProtocol::lcm(count, + mpStream->frameLenMax() - mpStream->frameLenMin() + 1); + } + + return count; +} diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..4c95da5 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,71 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" + +class PayloadProtocol : public AbstractProtocol +{ +public: + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Payload data; +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/payloadconfig.cpp b/common/payloadconfig.cpp new file mode 100644 index 0000000..d06d672 --- /dev/null +++ b/common/payloadconfig.cpp @@ -0,0 +1,84 @@ +/* +Copyright (C) 2010-2014 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 "payloadconfig.h" + +#include "payload.h" + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +PayloadConfigForm::~PayloadConfigForm() +{ +} + +AbstractProtocolConfigForm* PayloadConfigForm::createInstance() +{ + return new PayloadConfigForm; +} + +void PayloadConfigForm::loadWidget(AbstractProtocol *proto) +{ + cmbPatternMode->setCurrentIndex( + proto->fieldData( + PayloadProtocol::payload_dataPatternMode, + AbstractProtocol::FieldValue + ).toUInt()); + lePattern->setText(uintToHexStr( + proto->fieldData( + PayloadProtocol::payload_dataPattern, + AbstractProtocol::FieldValue + ).toUInt(), 4)); +} + +void PayloadConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + PayloadProtocol::payload_dataPatternMode, + cmbPatternMode->currentIndex()); + + proto->setFieldData( + PayloadProtocol::payload_dataPattern, + lePattern->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + + + diff --git a/common/payloadconfig.h b/common/payloadconfig.h new file mode 100644 index 0000000..8ebfeb0 --- /dev/null +++ b/common/payloadconfig.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _PAYLOAD_CONFIG_H +#define _PAYLOAD_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_payload.h" + +class PayloadConfigForm : + public AbstractProtocolConfigForm, + private Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); + virtual ~PayloadConfigForm(); + + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..e96b684 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,661 @@ +/* +Copyright (C) 2011 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 "pcapfileformat.h" + +#include "pdmlreader.h" +#include "ostprotolib.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; +const quint32 kDltEthernet = 1; + +PcapFileFormat pcapFileFormat; + +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + +PcapFileFormat::PcapFileFormat() +{ + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + + importDialog_ = NULL; +} + +PcapFileFormat::~PcapFileFormat() +{ + delete importDialog_; +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QTemporaryFile file2; + quint32 magic; + uchar gzipMagic[2]; + int len; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; + + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; + + emit status("Decompressing..."); + emit target(0); + + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + gzip.start(OstProtoLib::gzipPath(), + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + + fd_ >> magic; + + qDebug("magic = %08x", magic); + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); + else + fd_.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif + + pktBuf.resize(fileHdr.snapLen); + + if (importOptions_.value("ViaPdml").toBool()) + { + QProcess tshark; + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + diff.start(OstProtoLib::diffPath(), + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff. Check path in Preferences.\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + + goto _exit; + } + +_non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage + pktCount = 1; + while (!fd_.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + readPacket(pktHdr, pktBuf); + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; + pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; + } + + isOk = true; + goto _exit; + +_user_cancel: + isOk = true; + goto _exit; + +_diff_fail: + goto _exit; + +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +#if 0 +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + file.close(); + return isOk; +} + +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd_.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = kDltEthernet; + + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + emit status("Writing Packets..."); + emit target(streams.stream_size()); + + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += quint32(1e6/s.packetRate()); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } + + emit progress(i); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..064aaf1 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "abstractfileformat.h" +#include "ui_pcapfileimport.h" + +#include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; + +class PdmlReader; +class PcapFileFormat : public AbstractFileFormat +{ + friend class PdmlReader; + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + virtual QDialog* openOptionsDialog(); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..9ba1f2d --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,163 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" + +#include "ostprotolib.h" +#include "pdmlreader.h" + +#include +#include + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } + + goto _exit; + +_open_fail: + isOk = false; + +_user_cancel: +_exit: + delete reader; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; +} + +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + xml.readNext(); + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..e05026a --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "abstractfileformat.h" + +class PdmlFileFormat : public AbstractFileFormat +{ +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..a682aeb --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,248 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +/*! + \class PdmlProtocol + + PdmlProtocol is the base class which provides the interface for all + PDML decode helper protocols + + All Pdml helper classes derived from PdmlProtocol MUST register + themselves with PdmlReader. When PdmlReader encounters a 'proto' tag + in the PDML during parsing, it instantiates the corresponding helper + PdmlProtocol class and calls its methods to decode the protocol. + + A subclass MUST initialize the following inherited protected variables + in its constructor - + - ostProtoId_ + - fieldMap_ + + A subclass typically needs to reimplement the following methods - + - createInstance() + + Depending on certain conditions, subclasses may need to reimplement + the following additional methods - + - unknownFieldHandler() + - preProtocolHandler() + - postProtocolHandler() + + See the description of the methods for more information. + + Use the SamplePdmlProtocol implementation as boilerplate code and + for guidelines and tips +*/ + +/*! + Constructs the PdmlProtocol +*/ +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +/*! + Destroys the PdmlProtocol +*/ +PdmlProtocol::~PdmlProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function and register it with PdmlReader +*/ +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) +*/ +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +/*! + Returns true if name is a 'known' field that can be directly mapped + to the protobuf field +*/ +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +/*! + Returns the protocol's protobuf field number corresponding to name +*/ +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +/*! + This method is called by PdmlReader before any fields within the protocol + are processed. All attributes associated with the 'proto' tag in the PDML + are passed to this method + + Use this method to do any special handling that may be required for + preprocessing +*/ +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +/*! + This method is called by PdmlReader when it encounters a nested + protocol in the PDML i.e. a protocol within a protocol or a protocol + within a field + + This is a notification to the protocol that protocol processing will + be ending prematurely. postProtocolHandler() will still be called in + such cases. +*/ +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +/*! + This method is called by PdmlReader after all fields within the protocol + are processed. + + Use this method to do any special handling that may be required for + postprocessing +*/ +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + + +/*! + This method is called by PdmlReader for each field in the protocol + + Depending on whether it is a known or unknown field, the virtual methods + knownFieldHandler() and unknownFieldHandler() are invoked +*/ +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +/*! + Handles a 'known' field + + Uses protobuf reflection interface to set the protobuf field name to + valueHexStr as per the field's datatype +*/ +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +/*! + Handles a 'unknown' field + + The default implementation does nothing. Subclasses may need to implement + this if the protocol contains 'unknown' fields. +*/ +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..011fcb6 --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +const int kBaseHex = 16; + +class PdmlProtocol +{ +public: + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlProtocol(); + + //!< Protocol's field number as defined in message 'Protocol', enum 'k' + int ostProtoId_; + //!< Map of PDML field names to protobuf field numbers for 'known' fields + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp new file mode 100644 index 0000000..c64d46d --- /dev/null +++ b/common/pdmlprotocols.cpp @@ -0,0 +1,195 @@ +/* +Copyright (C) 2011 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 "pdmlprotocols.h" + +#include "hexdump.pb.h" + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +PdmlProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + endPos_ = pos; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + // Skipped field(s) at end? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), + pos, expPos_, endPos_); + + // Skipped field? Pad with zero! + if ((pos > expPos_) && (expPos_ < endPos_)) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ +} + +PdmlProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ +} + +PdmlProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h new file mode 100644 index 0000000..0747df1 --- /dev/null +++ b/common/pdmlprotocols.h @@ -0,0 +1,71 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H + +#include "pdmlprotocol.h" + +class PdmlUnknownProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlGenInfoProtocol(); + +}; + +class PdmlFrameProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..7b5eb9d --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,548 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdmlprotocols.h" + +#include "arppdml.h" +#include "eth2pdml.h" +#include "llcpdml.h" +#include "icmppdml.h" +#include "icmp6pdml.h" +#include "igmppdml.h" +#include "ip4pdml.h" +#include "ip6pdml.h" +#include "mldpdml.h" +#include "svlanpdml.h" +#include "tcppdml.h" +#include "textprotopdml.h" +#include "udppdml.h" +#include "vlanpdml.h" + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + QStringList creator; + + Q_ASSERT(isStartElement() && name() == "pdml"); + + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..7de3918 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + bool isMldSupport_; + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..9a74654 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,249 @@ +/* +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 +*/ + +package OstProto; +option cc_generic_services = true; + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; + optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; + optional double packets_per_sec = 9 [default = 1]; + optional double bursts_per_sec = 10 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +enum TransmitMode { + kSequentialTransmit = 0; + kInterleavedTransmit = 1; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; + optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; + + optional uint64 rx_drops = 100; + optional uint64 rx_errors = 101; + optional uint64 rx_fifo_errors = 102; + optional uint64 rx_frame_errors = 103; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTx(PortIdList) returns (Ack); + rpc stopTx(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..84e1e2d --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,232 @@ +/* +Copyright (C) 2010, 2014 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 "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" + +#include "mac.h" +#include "vlan.h" +#include "svlan.h" +#include "vlanstack.h" + +// L2 Protos +#include "dot3.h" +#include "llc.h" +#include "dot2llc.h" +#include "snap.h" +#include "dot2snap.h" +#include "eth2.h" + +// L3 Protos +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip4over4.h" +#include "ip4over6.h" +#include "ip6over4.h" +#include "ip6over6.h" + +// L4 Protos +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" + +// L5 Protos +#include "textproto.h" + +// Special Protos +#include "hexdump.h" +#include "payload.h" +#include "sample.h" +#include "userscript.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + // Layer 3 Protocols + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + // Layer 4 Protocols + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + + // Layer 5 Protocols + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + // Special Protocols + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + QString("No Protocol Creator registered for protocol %1") + .arg(protoNumber).toAscii().constData()); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..ff7279b --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,58 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + // TODO: make registerProtocol static + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp new file mode 100644 index 0000000..8c8e30a --- /dev/null +++ b/common/protocolwidgetfactory.cpp @@ -0,0 +1,193 @@ +/* +Copyright (C) 2014 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 "protocolwidgetfactory.h" + +#include "macconfig.h" +#include "vlanconfig.h" +#include "svlanconfig.h" +#include "vlanstackconfig.h" +// L2 Protocol Widgets +#include "eth2config.h" +#include "dot3config.h" +#include "llcconfig.h" +#include "dot2llcconfig.h" +#include "snapconfig.h" +#include "dot2snapconfig.h" +// L3 Protocol Widgets +#include "arpconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4over4config.h" +#include "ip4over6config.h" +#include "ip6over4config.h" +#include "ip6over6config.h" +// L4 Protocol Widgets +#include "icmpconfig.h" +#include "igmpconfig.h" +#include "mldconfig.h" +#include "tcpconfig.h" +#include "udpconfig.h" +// L5 Protocol Widgets +#include "textprotoconfig.h" +// Special Protocol Widgets +#include "hexdumpconfig.h" +#include "payloadconfig.h" +#include "sampleconfig.h" +#include "userscriptconfig.h" + +ProtocolWidgetFactory *OstProtocolWidgetFactory; +QMap ProtocolWidgetFactory::configWidgetFactory; + +ProtocolWidgetFactory::ProtocolWidgetFactory() +{ + /*! + * Ideally Protocol Widgets should register themselves + * with the Factory + */ + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMacFieldNumber, + (void*) MacConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanFieldNumber, + (void*) VlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kLlcFieldNumber, + (void*) LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSnapFieldNumber, + (void*) SnapConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapConfigForm::createInstance); + + // Layer 3 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kArpFieldNumber, + (void*) ArpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6ConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6ConfigForm::createInstance); + + // Layer 4 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMldFieldNumber, + (void*) MldConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTcpFieldNumber, + (void*) TcpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUdpFieldNumber, + (void*) UdpConfigForm::createInstance); + + // Layer 5 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocolConfigForm::createInstance); + + // Special Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSampleFieldNumber, + (void*) SampleConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptConfigForm::createInstance); +} + +ProtocolWidgetFactory::~ProtocolWidgetFactory() +{ + configWidgetFactory.clear(); +} + +void ProtocolWidgetFactory::registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator) +{ + Q_ASSERT(!configWidgetFactory.contains(protoNumber)); + + configWidgetFactory.insert(protoNumber, protoConfigWidgetInstanceCreator); +} + +AbstractProtocolConfigForm* ProtocolWidgetFactory::createConfigWidget( + int protoNumber) +{ + AbstractProtocolConfigForm* (*pc)(); + AbstractProtocolConfigForm* p; + + pc = (AbstractProtocolConfigForm* (*)()) + configWidgetFactory.value(protoNumber); + + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + QString(protoNumber).toAscii().constData()); + + p = (*pc)(); + + return p; +} + +void ProtocolWidgetFactory::deleteConfigWidget( + AbstractProtocolConfigForm *configWidget) +{ + delete configWidget; +} diff --git a/common/protocolwidgetfactory.h b/common/protocolwidgetfactory.h new file mode 100644 index 0000000..ebb69ec --- /dev/null +++ b/common/protocolwidgetfactory.h @@ -0,0 +1,46 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _PROTOCOL_WIDGET_FACTORY_H +#define _PROTOCOL_WIDGET_FACTORY_H + +#include + +class AbstractProtocolConfigForm; + +// Singleton class +class ProtocolWidgetFactory +{ + static QMap configWidgetFactory; + +public: + ProtocolWidgetFactory(); + ~ProtocolWidgetFactory(); + + // TODO: make registerProtocolConfigWidget static?? + // TODO: define a function pointer prototype instead of void* for + // protoConfigWidgetInstanceCreator + static void registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator); + + AbstractProtocolConfigForm* createConfigWidget(int protoNumber); + void deleteConfigWidget(AbstractProtocolConfigForm *configWidget); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..0432cf2 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,477 @@ +/* +Copyright (C) 2010, 2014 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 "sample.h" + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +SampleProtocol::~SampleProtocol() +{ +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xFFFFFF; + case ProtocolIdEth: return 0xFFFF; + case ProtocolIdIp: return 0xFF; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return AbstractProtocol::frameFieldCount(); +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | ((a & 0x07) << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (b & 0x1FFF)); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame has any variable fields or has a variable + size, return the minimum number of frames required to vary the fields \n + + Otherwise you don't need to reimplement this method - the base class always + returns 1 +*/ +int SampleProtocol::protocolFrameVariableCount() const +{ + return 1; +} diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..475e72f --- /dev/null +++ b/common/sample.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "abstractprotocol.h" +#include "sample.pb.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleProtocol : public AbstractProtocol +{ +public: + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Sample data; +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/sampleconfig.cpp b/common/sampleconfig.cpp new file mode 100644 index 0000000..caaf0d2 --- /dev/null +++ b/common/sampleconfig.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2010, 2014 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 "sampleconfig.h" +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SampleConfigForm::~SampleConfigForm() +{ +} + +SampleConfigForm* SampleConfigForm::createInstance() +{ + return new SampleConfigForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocolConfigForm::loadWidget() for more info +*/ +void SampleConfigForm::loadWidget(AbstractProtocol *proto) +{ + sampleA->setText( + proto->fieldData( + SampleProtocol::sample_a, + AbstractProtocol::FieldValue + ).toString()); + sampleB->setText( + proto->fieldData( + SampleProtocol::sample_b, + AbstractProtocol::FieldValue + ).toString()); + + samplePayloadLength->setText( + proto->fieldData( + SampleProtocol::sample_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isChecksumOverride->setChecked( + proto->fieldData( + SampleProtocol::sample_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + sampleChecksum->setText(uintToHexStr( + proto->fieldData( + SampleProtocol::sample_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + sampleX->setText( + proto->fieldData( + SampleProtocol::sample_x, + AbstractProtocol::FieldValue + ).toString()); + sampleY->setText( + proto->fieldData( + SampleProtocol::sample_y, + AbstractProtocol::FieldValue + ).toString()); +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocolConfigForm::storeWidget() for more info +*/ +void SampleConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SampleProtocol::sample_a, + sampleA->text()); + proto->setFieldData( + SampleProtocol::sample_b, + sampleB->text()); + + proto->setFieldData( + SampleProtocol::sample_payloadLength, + samplePayloadLength->text()); + proto->setFieldData( + SampleProtocol::sample_is_override_checksum, + + isChecksumOverride->isChecked()); + proto->setFieldData( + SampleProtocol::sample_checksum, + hexStrToUInt(sampleChecksum->text())); + + proto->setFieldData( + SampleProtocol::sample_x, + sampleX->text()); + proto->setFieldData( + SampleProtocol::sample_y, + sampleY->text()); +} + diff --git a/common/sampleconfig.h b/common/sampleconfig.h new file mode 100644 index 0000000..8e85976 --- /dev/null +++ b/common/sampleconfig.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _SAMPLE_CONFIG_H +#define _SAMPLE_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_sample.h" + +class SampleConfigForm : + public AbstractProtocolConfigForm, + private Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); + virtual ~SampleConfigForm(); + + static SampleConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: +}; + +#endif diff --git a/common/samplepdml.cpp b/common/samplepdml.cpp new file mode 100644 index 0000000..99b671b --- /dev/null +++ b/common/samplepdml.cpp @@ -0,0 +1,118 @@ +/* +Copyright (C) 2014 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 "samplepdml.h" + +#include "sample.pb.h" + +/*! + TODO : Initialize the following inherited protected members - + - ostProtoId_ + - fieldMap_ + + ostProtoId_ is the protocol's protobuf field number as defined in + message 'Protocol' enum 'k' in file protocol.proto + + fieldMap_ is a mapping of the protocol's field names as they appear + in the PDML to the protobuf field numbers for the protocol. All such + fields are classified as 'known' fields and the base class will take care + of decoding these without any help from the subclass. + + Note that the PDML field names are same as the field names used in Wireshark + display filters. The full reference for these is available at - + http://www.wireshark.org/docs/dfref/ +*/ +PdmlSampleProtocol::PdmlSampleProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + fieldMap_.insert("sample.checksum", OstProto::Sample::kChecksumFieldNumber); + fieldMap_.insert("sample.x", OstProto::Sample::kXFieldNumber); + fieldMap_.insert("sample.y", OstProto::Sample::kYFieldNumber); +} + +PdmlSampleProtocol::~PdmlSampleProtocol() +{ +} + +PdmlSampleProtocol* PdmlSampleProtocol::createInstance() +{ + return new PdmlSampleProtocol(); +} + +/*! + TODO: Use this method to do any special handling that may be required for + preprocessing a protocol before parsing/decoding the protocol's fields +*/ +void PdmlSampleProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling or cleanup that may be + required when a protocol decode is ending prematurely +*/ +void PdmlSampleProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling that may be required for + postprocessing a protocol after parsing/decoding all the protocol fields + + If your protocol's protobuf has some meta-fields that should be set to + their non default values, this is a good place to do that. e.g. derived + fields such as length, checksum etc. may be correct or incorrect in the + PCAP/PDML - to retain the same value as in the PCAP/PDML and not let + Ostinato recalculate these, you can set the is_override_length, + is_override_cksum meta-fields to true here +*/ +void PdmlSampleProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Handle all 'unknown' fields using this method + + You need to typically only handle frame fields or fields actually present + in the protocol on the wire. So you can safely ignore meta-fields such as + Good/Bad Checksum. + + Some fields may not have a 'name' attribute, so cannot be classified as + a 'known' field. Use this method to identify such fields using other + attributes such as 'show' or 'showname' and populate the corresponding + protobuf field. + + If the PDML protocol contains some fields that are not supported by Ostinato, + use a HexDump protocol as a replacement to store these bytes +*/ +void PdmlSampleProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} diff --git a/common/samplepdml.h b/common/samplepdml.h new file mode 100644 index 0000000..a07965b --- /dev/null +++ b/common/samplepdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SAMPLE_PDML_H +#define _SAMPLE_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSampleProtocol : public PdmlProtocol +{ +public: + virtual ~PdmlSampleProtocol(); + + static PdmlSampleProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlSampleProtocol(); +}; + +#endif diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..6e7e7cc --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,255 @@ +/* +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 +*/ + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +SnapProtocol::~SnapProtocol() +{ +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..bf3a349 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,70 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" + +class SnapProtocol : public AbstractProtocol +{ +public: + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Snap data; +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/snapconfig.cpp b/common/snapconfig.cpp new file mode 100644 index 0000000..e594b57 --- /dev/null +++ b/common/snapconfig.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "snapconfig.h" +#include "snap.h" + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SnapConfigForm::~SnapConfigForm() +{ +} + +SnapConfigForm* SnapConfigForm::createInstance() +{ + return new SnapConfigForm; +} + +void SnapConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideOui->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_oui, + AbstractProtocol::FieldValue + ).toBool()); + leOui->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_oui, + AbstractProtocol::FieldValue + ).toUInt(), 3)); + + cbOverrideType->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void SnapConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SnapProtocol::snap_is_override_oui, + cbOverrideOui->isChecked()); + proto->setFieldData( + SnapProtocol::snap_oui, + hexStrToUInt(leOui->text())); + + proto->setFieldData( + SnapProtocol::snap_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + SnapProtocol::snap_type, + hexStrToUInt(leType->text())); +} diff --git a/common/snapconfig.h b/common/snapconfig.h new file mode 100644 index 0000000..9d230e3 --- /dev/null +++ b/common/snapconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _SNAP_CONFIG_H +#define _SNAP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_snap.h" + +class SnapConfigForm : + public AbstractProtocolConfigForm, + private Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); + virtual ~SnapConfigForm(); + + static SnapConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..ab43117 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,565 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +/*! Convenience Function */ +quint16 StreamBase::frameLenAvg() const +{ + quint16 avgFrameLen; + + if (lenMode() == e_fl_fixed) + avgFrameLen = frameLen(); + else + avgFrameLen = (frameLenMin() + frameLenMax())/2; + + return avgFrameLen; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +double StreamBase::packetRate() const +{ + return (double) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(double packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +double StreamBase::burstRate() const +{ + return (double) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(double burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +/*! Convenience Function */ +double StreamBase::averagePacketRate() const +{ + double avgPacketRate; + + switch (sendUnit()) + { + case e_su_bursts: + avgPacketRate = burstRate() * burstSize(); + break; + case e_su_packets: + avgPacketRate = packetRate(); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return avgPacketRate; +} + +/*! Convenience Function */ +bool StreamBase::setAveragePacketRate(double packetsPerSec) +{ + switch (sendUnit()) + { + case e_su_bursts: + setBurstRate(packetsPerSec/double(burstSize())); + break; + case e_su_packets: + setPacketRate(packetsPerSec); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +int StreamBase::frameVariableCount() const +{ + ProtocolListIterator *iter; + quint64 frameCount = 1; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + int count; + + proto = iter->next(); + count = proto->protocolFrameVariableCount(); + + // correct count for mis-behaving protocols + if (count <= 0) + count = 1; + + frameCount = AbstractProtocol::lcm(frameCount, count); + } + delete iter; + + return frameCount; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..9ef3ba1 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + quint16 frameLenAvg() const; + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + double packetRate() const; + bool setPacketRate(double packetsPerSec); + + double burstRate() const; + bool setBurstRate(double burstsPerSec); + + double averagePacketRate() const; + bool setAveragePacketRate(double packetsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameVariableCount() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..9b5f65b --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,66 @@ +/* +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 +*/ + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/svlanconfig.h b/common/svlanconfig.h new file mode 100644 index 0000000..e5bd578 --- /dev/null +++ b/common/svlanconfig.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SVLAN_CONFIG_H +#define _SVLAN_CONFIG_H + +#include "vlanconfig.h" + +typedef VlanConfigForm SVlanConfigForm; + +#endif + diff --git a/common/svlanpdml.cpp b/common/svlanpdml.cpp new file mode 100644 index 0000000..113f515 --- /dev/null +++ b/common/svlanpdml.cpp @@ -0,0 +1,110 @@ +/* +Copyright (C) 2011 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 "svlanpdml.h" + +#include "eth2.pb.h" +#include "svlan.pb.h" + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/svlanpdml.h b/common/svlanpdml.h new file mode 100644 index 0000000..517cc0f --- /dev/null +++ b/common/svlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SVLAN_PDML_H +#define _SVLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +#endif diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..2f9a5b4 --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,606 @@ +/* +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 +*/ + +#include "tcp.h" + + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +TcpProtocol::~TcpProtocol() +{ +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int TcpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..276ba65 --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpProtocol : public AbstractProtocol +{ +public: + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Tcp data; +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/tcpconfig.cpp b/common/tcpconfig.cpp new file mode 100644 index 0000000..ff08f7d --- /dev/null +++ b/common/tcpconfig.cpp @@ -0,0 +1,175 @@ +/* +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 +*/ + +#include "tcpconfig.h" +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +TcpConfigForm::~TcpConfigForm() +{ +} + +TcpConfigForm* TcpConfigForm::createInstance() +{ + return new TcpConfigForm; +} + +void TcpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leTcpSrcPort->setText( + proto->fieldData( + TcpProtocol::tcp_src_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpSrcPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_src_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpDstPort->setText( + proto->fieldData( + TcpProtocol::tcp_dst_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpDstPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_dst_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpSeqNum->setText( + proto->fieldData( + TcpProtocol::tcp_seq_num, + AbstractProtocol::FieldValue + ).toString()); + leTcpAckNum->setText( + proto->fieldData( + TcpProtocol::tcp_ack_num, + AbstractProtocol::FieldValue + ).toString()); + + leTcpHdrLen->setText( + proto->fieldData( + TcpProtocol::tcp_hdrlen, + AbstractProtocol::FieldValue + ).toString()); + cbTcpHdrLenOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_hdrlen, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpWindow->setText( + proto->fieldData( + TcpProtocol::tcp_window, + AbstractProtocol::FieldValue + ).toString()); + + leTcpCksum->setText(uintToHexStr( + proto->fieldData( + TcpProtocol::tcp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbTcpCksumOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_cksum, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpUrgentPointer->setText( + proto->fieldData( + TcpProtocol::tcp_urg_ptr, + AbstractProtocol::FieldValue + ).toString()); + + uint flags = proto->fieldData( + TcpProtocol::tcp_flags, + AbstractProtocol::FieldValue + ).toUInt(); + + cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpConfigForm::storeWidget(AbstractProtocol *proto) +{ + int ff = 0; + + proto->setFieldData( + TcpProtocol::tcp_src_port, + leTcpSrcPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_src_port, + cbTcpSrcPortOverride->isChecked()); + proto->setFieldData( + TcpProtocol::tcp_dst_port, + leTcpDstPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_dst_port, + cbTcpDstPortOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_seq_num, + leTcpSeqNum->text()); + proto->setFieldData( + TcpProtocol::tcp_ack_num, + leTcpAckNum->text()); + + proto->setFieldData( + TcpProtocol::tcp_hdrlen, + leTcpHdrLen->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_hdrlen, + cbTcpHdrLenOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_window, + leTcpWindow->text()); + + proto->setFieldData( + TcpProtocol::tcp_cksum, + hexStrToUInt(leTcpCksum->text())); + proto->setFieldData( + TcpProtocol::tcp_is_override_cksum, + cbTcpCksumOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_urg_ptr, + leTcpUrgentPointer->text()); + + if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + + proto->setFieldData(TcpProtocol::tcp_flags, ff); +} + diff --git a/common/tcpconfig.h b/common/tcpconfig.h new file mode 100644 index 0000000..859238a --- /dev/null +++ b/common/tcpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TCP_CONFIG_H +#define _TCP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_tcp.h" + +class TcpConfigForm : + public AbstractProtocolConfigForm, + private Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); + virtual ~TcpConfigForm(); + + static TcpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/tcppdml.cpp b/common/tcppdml.cpp new file mode 100644 index 0000000..3980b6a --- /dev/null +++ b/common/tcppdml.cpp @@ -0,0 +1,98 @@ +/* +Copyright (C) 2011 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 "tcppdml.h" + +#include "hexdump.pb.h" +#include "tcp.pb.h" + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/tcppdml.h b/common/tcppdml.h new file mode 100644 index 0000000..5c3d1c6 --- /dev/null +++ b/common/tcppdml.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TCP_PDML_H +#define _TCP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +#endif diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..285668d --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,240 @@ +/* +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 +*/ + +#include "textproto.h" + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +TextProtocol::~TextProtocol() +{ +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..8c00e47 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,77 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "abstractprotocol.h" +#include "textproto.pb.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocol : public AbstractProtocol +{ +public: + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + +private: + OstProto::TextProtocol data; +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..6e7ebdb --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProto + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + +
    diff --git a/common/textprotoconfig.cpp b/common/textprotoconfig.cpp new file mode 100644 index 0000000..0242ffa --- /dev/null +++ b/common/textprotoconfig.cpp @@ -0,0 +1,84 @@ +/* +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 +*/ + +#include "textprotoconfig.h" +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocolConfigForm::~TextProtocolConfigForm() +{ +} + +TextProtocolConfigForm* TextProtocolConfigForm::createInstance() +{ + return new TextProtocolConfigForm; +} + +void TextProtocolConfigForm::loadWidget(AbstractProtocol *proto) +{ + portNumCombo->setValue( + proto->fieldData( + TextProtocol::textProto_portNum, + AbstractProtocol::FieldValue + ).toUInt()); + eolCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_eol, + AbstractProtocol::FieldValue + ).toUInt()); + encodingCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_encoding, + AbstractProtocol::FieldValue + ).toUInt()); + protoText->setText( + proto->fieldData( + TextProtocol::textProto_text, + AbstractProtocol::FieldValue + ).toString()); +} + +void TextProtocolConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + TextProtocol::textProto_portNum, + portNumCombo->currentValue()); + proto->setFieldData( + TextProtocol::textProto_eol, + eolCombo->currentIndex()); + proto->setFieldData( + TextProtocol::textProto_encoding, + encodingCombo->currentIndex()); + + proto->setFieldData( + TextProtocol::textProto_text, + protoText->toPlainText()); +} + diff --git a/common/textprotoconfig.h b/common/textprotoconfig.h new file mode 100644 index 0000000..a1971ab --- /dev/null +++ b/common/textprotoconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_CONFIG_H +#define _TEXT_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_textproto.h" + +class TextProtocolConfigForm : + public AbstractProtocolConfigForm, + private Ui::TextProto +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); + virtual ~TextProtocolConfigForm(); + + static TextProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/textprotopdml.cpp b/common/textprotopdml.cpp new file mode 100644 index 0000000..26ccfe7 --- /dev/null +++ b/common/textprotopdml.cpp @@ -0,0 +1,171 @@ +/* +Copyright (C) 2011 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 "textprotopdml.h" + +#include "textproto.pb.h" + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/textprotopdml.h b/common/textprotopdml.h new file mode 100644 index 0000000..daaca0c --- /dev/null +++ b/common/textprotopdml.h @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TEXT_PROTO_PDML_H +#define _TEXT_PROTO_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..f3d051c --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,431 @@ +/* +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 +*/ + +#include "udp.h" + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +UdpProtocol::~UdpProtocol() +{ +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int UdpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..56285b4 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" +#include "udp.pb.h" + +class UdpProtocol : public AbstractProtocol +{ +public: + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Udp data; +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/udpconfig.cpp b/common/udpconfig.cpp new file mode 100644 index 0000000..dab05ec --- /dev/null +++ b/common/udpconfig.cpp @@ -0,0 +1,114 @@ +/* +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 +*/ + +#include "udpconfig.h" +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +UdpConfigForm::~UdpConfigForm() +{ +} + +UdpConfigForm* UdpConfigForm::createInstance() +{ + return new UdpConfigForm; +} + + +void UdpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leUdpSrcPort->setText( + proto->fieldData( + UdpProtocol::udp_srcPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpSrcPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideSrcPort, + AbstractProtocol::FieldValue + ).toBool()); + leUdpDstPort->setText( + proto->fieldData( + UdpProtocol::udp_dstPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpDstPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideDstPort, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpLength->setText( + proto->fieldData( + UdpProtocol::udp_totLen, + AbstractProtocol::FieldValue + ).toString()); + cbUdpLengthOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpCksum->setText(uintToHexStr( + proto->fieldData( + UdpProtocol::udp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbUdpCksumOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); +} + +void UdpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UdpProtocol::udp_srcPort, + leUdpSrcPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideSrcPort, + cbUdpSrcPortOverride->isChecked()); + proto->setFieldData( + UdpProtocol::udp_dstPort, + leUdpDstPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideDstPort, + cbUdpDstPortOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_totLen, + leUdpLength->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideTotLen, + cbUdpLengthOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_cksum, + hexStrToUInt(leUdpCksum->text())); + proto->setFieldData( + UdpProtocol::udp_isOverrideCksum, + cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udpconfig.h b/common/udpconfig.h new file mode 100644 index 0000000..98b8ecb --- /dev/null +++ b/common/udpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _UDP_CONFIG_H +#define _UDP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_udp.h" + +class UdpConfigForm : + public AbstractProtocolConfigForm, + private Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); + virtual ~UdpConfigForm(); + + static UdpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/udppdml.cpp b/common/udppdml.cpp new file mode 100644 index 0000000..0cb1685 --- /dev/null +++ b/common/udppdml.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 "udppdml.h" + +#include "udp.pb.h" + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + diff --git a/common/udppdml.h b/common/udppdml.h new file mode 100644 index 0000000..3ae1d6e --- /dev/null +++ b/common/udppdml.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _UDP_PDML_H +#define _UDP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +#endif diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..f00ab6c --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,555 @@ +/* +Copyright (C) 2010, 2014 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 "userscript.h" + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + evaluateUserScript(); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +int UserScriptProtocol::protocolFrameVariableCount() const +{ + return userProtocol_.protocolFrameVariableCount(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; + protocolFrameVariableCount_ = 1; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +int UserProtocol::protocolFrameVariableCount() const +{ + return protocolFrameVariableCount_; +} + +void UserProtocol::setProtocolFrameVariableCount(int count) +{ + protocolFrameVariableCount_ = count; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +int UserProtocol::protocolFramePayloadVariableCount() const +{ + return parent_->protocolFramePayloadVariableCount(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..f6bf6ef --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,162 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + Q_PROPERTY(int protocolFrameVariableCount + READ protocolFrameVariableCount + WRITE setProtocolFrameVariableCount); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp, + ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + int protocolFrameVariableCount() const; + void setProtocolFrameVariableCount(int count); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; + int protocolFrameVariableCount_; +}; + +class UserScriptProtocol : public AbstractProtocol +{ +public: + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + OstProto::UserScript data; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/userscriptconfig.cpp b/common/userscriptconfig.cpp new file mode 100644 index 0000000..85e0aba --- /dev/null +++ b/common/userscriptconfig.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2010, 2014 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 "userscriptconfig.h" +#include "userscript.h" + +#include + +UserScriptConfigForm::UserScriptConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + // The protocol_ (UserScriptProtocol) is a dummy protocol internal + // to UserScriptConfigForm whose sole purpose is to "compile" the script + // so that the configForm widget can display the compilation result. + // It is *not* used for actual packet contents at any time + protocol_ = new UserScriptProtocol(NULL); + compileScript(); +} + +UserScriptConfigForm::~UserScriptConfigForm() +{ + delete protocol_; +} + +UserScriptConfigForm* UserScriptConfigForm::createInstance() +{ + return new UserScriptConfigForm; +} + +void UserScriptConfigForm::loadWidget(AbstractProtocol *proto) +{ + programEdit->setPlainText( + proto->fieldData( + UserScriptProtocol::userScript_program, + AbstractProtocol::FieldValue + ).toString()); + + compileScript(); +} + +void UserScriptConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UserScriptProtocol::userScript_program, + programEdit->toPlainText()); +} + +// +// ----- private methods +// +void UserScriptConfigForm::compileScript() +{ + // storeWidget() will save the updated userscript into + // the protocol_ which in turn triggers the protocol_ to + // compile it + storeWidget(protocol_); + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +// +// ----- private slots +// +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + compileScript(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } +} + diff --git a/common/userscriptconfig.h b/common/userscriptconfig.h new file mode 100644 index 0000000..8285103 --- /dev/null +++ b/common/userscriptconfig.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _USER_SCRIPT_CONFIG_H +#define _USER_SCRIPT_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_userscript.h" + +class UserScriptProtocol; + +class UserScriptConfigForm : + public AbstractProtocolConfigForm, + private Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(QWidget *parent = 0); + virtual ~UserScriptConfigForm(); + + static UserScriptConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + void compileScript(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; +#endif diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..cb07cd2 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,270 @@ +/* +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 +*/ + +#include "vlan.h" + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +VlanProtocol::~VlanProtocol() +{ +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + switch(attrib) + { + case FieldValue: return data.is_override_tpid(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case vlan_tpid: + { + uint tpid = value.toUInt(&isOk); + if (isOk) + data.set_tpid(tpid); + break; + } + case vlan_prio: + { + uint prio = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((prio & 0x07) << 13) | (data.vlan_tag() & 0x1FFF)); + break; + } + case vlan_cfiDei: + { + uint cfiDei = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((cfiDei & 0x01) << 12) | (data.vlan_tag() & 0xEFFF)); + break; + } + case vlan_vlanId: + { + uint vlanId = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + (vlanId & 0x0FFF) | (data.vlan_tag() & 0xF000)); + break; + } + + // Meta-Fields + case vlan_isOverrideTpid: + { + bool override = value.toUInt(&isOk); + if (isOk) + data.set_is_override_tpid(override); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..26cf874 --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,67 @@ +/* +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 +*/ + +#ifndef _VLAN_H +#define _VLAN_H + +#include "abstractprotocol.h" +#include "vlan.pb.h" + +class VlanProtocol : public AbstractProtocol +{ +public: + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + OstProto::Vlan data; +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..72e49c0 --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,181 @@ + + Vlan + + + + 0 + 0 + 274 + 106 + + + + Form + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + Qt::Horizontal + + + + 111 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanconfig.cpp b/common/vlanconfig.cpp new file mode 100644 index 0000000..35241bd --- /dev/null +++ b/common/vlanconfig.cpp @@ -0,0 +1,87 @@ +/* +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 +*/ + +#include "vlanconfig.h" +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +VlanConfigForm::~VlanConfigForm() +{ +} + +VlanConfigForm* VlanConfigForm::createInstance() +{ + return new VlanConfigForm; +} + +void VlanConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbTpidOverride->setChecked( + proto->fieldData( + VlanProtocol::vlan_isOverrideTpid, + AbstractProtocol::FieldValue + ).toBool()); + leTpid->setText(uintToHexStr( + proto->fieldData( + VlanProtocol::vlan_tpid, + AbstractProtocol::FieldValue) + .toUInt(), 2)); + cmbPrio->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_prio, + AbstractProtocol::FieldValue) + .toUInt()); + cmbCfiDei->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_cfiDei, + AbstractProtocol::FieldValue) + .toUInt()); + leVlanId->setText( + proto->fieldData( + VlanProtocol::vlan_vlanId, + AbstractProtocol::FieldValue) + .toString()); +} + +void VlanConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + VlanProtocol::vlan_isOverrideTpid, + cbTpidOverride->isChecked()); + proto->setFieldData( + VlanProtocol::vlan_tpid, + leTpid->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + proto->setFieldData( + VlanProtocol::vlan_prio, + cmbPrio->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_cfiDei, + cmbCfiDei->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_vlanId, + leVlanId->text()); +} + diff --git a/common/vlanconfig.h b/common/vlanconfig.h new file mode 100644 index 0000000..a761873 --- /dev/null +++ b/common/vlanconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _VLAN_CONFIG_H +#define _VLAN_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_vlan.h" + +class VlanConfigForm : + public AbstractProtocolConfigForm, + private Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); + virtual ~VlanConfigForm(); + + static VlanConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/vlanpdml.cpp b/common/vlanpdml.cpp new file mode 100644 index 0000000..722d038 --- /dev/null +++ b/common/vlanpdml.cpp @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 "vlanpdml.h" + +#include "eth2.pb.h" +#include "vlan.pb.h" + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/vlanpdml.h b/common/vlanpdml.h new file mode 100644 index 0000000..6bab024 --- /dev/null +++ b/common/vlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _VLAN_PDML_H +#define _VLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +#endif diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/common/vlanstackconfig.h b/common/vlanstackconfig.h new file mode 100644 index 0000000..5203824 --- /dev/null +++ b/common/vlanstackconfig.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _VLAN_STACK_CONFIG_H +#define _VLAN_STACK_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "svlanconfig.h" +#include "vlanconfig.h" +#include "svlan.h" +#include "vlan.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kVlanStackFieldNumber, + SVlanConfigForm, VlanConfigForm, + SVlanProtocol, VlanProtocol + > VlanStackConfigForm; + +#endif diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..89479e7 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/commands.h\ + src/qhexedit.h \ + src/qhexedit_p.h \ + src/xbytearray.h + +SOURCES = src/commands.cpp \ + src/qhexedit.cpp \ + src/qhexedit_p.cpp \ + src/xbytearray.cpp diff --git a/extra/qhexedit2/src/commands.cpp b/extra/qhexedit2/src/commands.cpp new file mode 100644 index 0000000..303091d --- /dev/null +++ b/extra/qhexedit2/src/commands.cpp @@ -0,0 +1,115 @@ +#include "commands.h" + +CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _xData = xData; + _charPos = charPos; + _newChar = newChar; + _cmd = cmd; +} + +bool CharCommand::mergeWith(const QUndoCommand *command) +{ + const CharCommand *nextCommand = static_cast(command); + bool result = false; + + if (_cmd != remove) + { + if (nextCommand->_cmd == replace) + if (nextCommand->_charPos == _charPos) + { + _newChar = nextCommand->_newChar; + result = true; + } + } + return result; +} + +void CharCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_charPos, 1); + break; + case replace: + _xData->replace(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + case remove: + _xData->insert(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + } +} + +void CharCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_charPos, _newChar); + break; + case replace: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->replace(_charPos, _newChar); + break; + case remove: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->remove(_charPos, 1); + break; + } +} + + + +ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _cmd = cmd; + _xData = xData; + _baPos = baPos; + _newBa = newBa; + _len = len; +} + +void ArrayCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_baPos, _newBa.length()); + break; + case replace: + _xData->replace(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + case remove: + _xData->insert(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + } +} + +void ArrayCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_baPos, _newBa); + break; + case replace: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->replace(_baPos, _newBa); + break; + case remove: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->remove(_baPos, _len); + break; + } +} diff --git a/extra/qhexedit2/src/commands.h b/extra/qhexedit2/src/commands.h new file mode 100644 index 0000000..9931b3f --- /dev/null +++ b/extra/qhexedit2/src/commands.h @@ -0,0 +1,70 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +/** \cond docNever */ + +#include + +#include "xbytearray.h" + +/*! CharCommand is a class to prived undo/redo functionality in QHexEdit. +A QUndoCommand represents a single editing action on a document. CharCommand +is responsable for manipulations on single chars. It can insert. replace and +remove characters. A manipulation stores allways to actions +1. redo (or do) action +2. undo action. + +CharCommand also supports command compression via mergeWidht(). This allows +the user to execute a undo command contation e.g. 3 steps in a single command. +If you for example insert a new byt "34" this means for the editor doing 3 +steps: insert a "00", replace it with "03" and the replace it with "34". These +3 steps are combined into a single step, insert a "34". +*/ +class CharCommand : public QUndoCommand +{ +public: + enum { Id = 1234 }; + enum Cmd {insert, remove, replace}; + + CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, + QUndoCommand *parent=0); + + void undo(); + void redo(); + bool mergeWith(const QUndoCommand *command); + int id() const { return Id; } + +private: + XByteArray * _xData; + int _charPos; + bool _wasChanged; + char _newChar; + char _oldChar; + Cmd _cmd; +}; + +/*! ArrayCommand provides undo/redo functionality for handling binary strings. It +can undo/redo insert, replace and remove binary strins (QByteArrays). +*/ +class ArrayCommand : public QUndoCommand +{ +public: + enum Cmd {insert, remove, replace}; + ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, + QUndoCommand *parent=0); + void undo(); + void redo(); + +private: + Cmd _cmd; + XByteArray * _xData; + int _baPos; + int _len; + QByteArray _wasChanged; + QByteArray _newBa; + QByteArray _oldBa; +}; + +/** \endcond docNever */ + +#endif // COMMANDS_H diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..b6dd38d --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,152 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); + connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); + setFocusPolicy(Qt::NoFocus); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +QString QHexEdit::toReadableString() +{ + return qHexEdit_p->toRedableString(); +} + +QString QHexEdit::selectionToReadableString() +{ + return qHexEdit_p->selectionToReadableString(); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::redo() +{ + qHexEdit_p->redo(); +} + +void QHexEdit::undo() +{ + qHexEdit_p->undo(); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return qHexEdit_p->addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setSelectionColor(const QColor &color) +{ + qHexEdit_p->setSelectionColor(color); +} + +QColor QHexEdit::selectionColor() +{ + return qHexEdit_p->selectionColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setReadOnly(bool readOnly) +{ + qHexEdit_p->setReadOnly(readOnly); +} + +bool QHexEdit::isReadOnly() +{ + return qHexEdit_p->isReadOnly(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + +const QFont & QHexEdit::font() const +{ + return qHexEdit_p->font(); +} diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..b5a9601 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,205 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.6.1 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text +data. There are sip configuration files included, so it is easy to create +bindings for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use +the mouse or the keyboard to navigate inside the widget. If you hit the keys +(0..9, a..f) you will change the data. Changed data is highlighted and can be +accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) +and insert data. In this case the size of data() increases. It is also possible +to delete bytes (del or backspace), here the size of data decreases. + +You can select data with keyboard hits or mouse movements. The copy-key will +copy the selected data into the clipboard. The cut-key copies also but delets +it afterwards. In overwrite mode, the paste function overwrites the content of +the (does not change the length) data. In insert mode, clipboard data will be +inserted. The clipboard content is expected in ASCII Hex notation. Unknown +characters will be ignored. + +QHexEdit comes with undo/redo functionality. All changes can be undone, by +pressing the undo-key (usually ctr-z). They can also be redone afterwards. +The undo/redo framework is cleared, when setData() sets up a new +content for the editor. + +This widget can only handle small amounts of data. The size has to be below 10 +megabytes, otherwise the scroll sliders ard not shown and you can't scroll any +more. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Property selection color sets (setSelectionColor()) the backgorund + color of selected text areas. You can also read the color + (selectionColor()). + */ + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwrite mode the user will overwrite existing data. The + size of data will be constant. In insert mode the size will grow, when inserting + new data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + + /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode + in which the editor works. In readonly mode the the user can only navigate + through the data and select data; modifying is not possible. This + property's default is false. + */ + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + Q_PROPERTY(QFont font READ font WRITE setFont) + + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + In overwrite mode, the existing bytes will be overwriten with 0x00. + */ + void remove(int pos, int len=1); + + /*! Gives back a formatted image of the content of QHexEdit + */ + QString toReadableString(); + + /*! Gives back a formatted image of the selected content of QHexEdit + */ + QString selectionToReadableString(); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setSelectionColor(QColor const &color); + QColor selectionColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + void setReadOnly(bool); + bool isReadOnly(); + const QFont &font() const; + void setFont(const QFont &); + /*! \endcond docNever */ + +public slots: + /*! Redoes the last operation. If there is no operation to redo, i.e. + there is no redo step in the undo/redo history, nothing happens. + */ + void redo(); + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + + /*! Undoes the last operation. If there is no operation to undo, i.e. + there is no undo step in the undo/redo history, nothing happens. + */ + void undo(); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddressChanged(int address); + + /*! Contains the size of the data to edit. */ + void currentSizeChanged(int size); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..2f046bb --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,800 @@ +#include + +#include "qhexedit_p.h" +#include "commands.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _undoStack = new QUndoStack(this); + + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setReadOnly(false); + setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); + setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); + setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); + setFont(QFont("Courier", 10)); + + _size = 0; + resetSelection(0); + + setFocusPolicy(Qt::StrongFocus); + + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + _cursorTimer.setInterval(500); + _cursorTimer.start(); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _xData.setAddressOffset(offset); + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _xData.addressOffset(); +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _xData.setData(data); + _undoStack->clear(); + adjust(); + setCursorPos(0); +} + +QByteArray QHexEditPrivate::data() +{ + return _xData.data(); +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setSelectionColor(const QColor &color) +{ + _selectionColor = color; + update(); +} + +QColor QHexEditPrivate::selectionColor() +{ + return _selectionColor; +} + +void QHexEditPrivate::setReadOnly(bool readOnly) +{ + _readOnly = readOnly; +} + +bool QHexEditPrivate::isReadOnly() +{ + return _readOnly; +} + +XByteArray & QHexEditPrivate::xData() +{ + return _xData; +} + +void QHexEditPrivate::insert(int index, const QByteArray & ba) +{ + if (ba.length() > 0) + { + if (_overwriteMode) + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } +} + +void QHexEditPrivate::insert(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::remove(int index, int len) +{ + if (len > 0) + { + if (len == 1) + { + if (_overwriteMode) + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + else + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + } + else + { + QByteArray ba = QByteArray(len, char(0)); + if (_overwriteMode) + { + QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } + } +} + +void QHexEditPrivate::replace(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::replace(int index, const QByteArray & ba) +{ + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + _xData.setAddressWidth(addressWidth); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + _overwriteMode = overwriteMode; +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::redo() +{ + _undoStack->redo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +void QHexEditPrivate::undo() +{ + _undoStack->undo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +QString QHexEditPrivate::toRedableString() +{ + return _xData.toRedableString(); +} + + +QString QHexEditPrivate::selectionToReadableString() +{ + return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + +/*****************************************************************************/ +/* Cursor movements */ +/*****************************************************************************/ + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + { + setCursorPos(_cursorPosition - 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfLine)) + { + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfLine)) + { + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousLine)) + { + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousPage)) + { + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_xData.size() * 2); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfDocument)) + { + setCursorPos(0); + resetSelection(_cursorPosition); + } + +/*****************************************************************************/ +/* Select commands */ +/*****************************************************************************/ + if (event->matches(QKeySequence::SelectAll)) + { + resetSelection(0); + setSelection(2*_xData.size() + 1); + } + if (event->matches(QKeySequence::SelectNextChar)) + { + int pos = _cursorPosition + 1; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousChar)) + { + int pos = _cursorPosition - 1; + setSelection(pos); + setCursorPos(pos); + } + if (event->matches(QKeySequence::SelectEndOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousLine)) + { + int pos = _cursorPosition - (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectNextLine)) + { + int pos = _cursorPosition + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + + if (event->matches(QKeySequence::SelectNextPage)) + { + int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousPage)) + { + int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectEndOfDocument)) + { + int pos = _xData.size() * 2; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfDocument)) + { + int pos = 0; + setCursorPos(pos); + setSelection(pos); + } + +/*****************************************************************************/ +/* Edit Commands */ +/*****************************************************************************/ +if (!_readOnly) +{ + /* Hex input */ + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + + // If insert mode, then insert a byte + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + } + + // Change content + if (_xData.size() > 0) + { + QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + + replace(posBa, QByteArray().fromHex(hexValue)[0]); + + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + } + + /* Cut & Paste */ + if (event->matches(QKeySequence::Cut)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append("\n"); + } + remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + setCursorPos(getSelectionBegin()); + resetSelection(getSelectionBegin()); + } + + if (event->matches(QKeySequence::Paste)) + { + QClipboard *clipboard = QApplication::clipboard(); + QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); + insert(_cursorPosition / 2, ba); + setCursorPos(_cursorPosition + 2 * ba.length()); + resetSelection(getSelectionBegin()); + } + + + /* Delete char */ + if (event->matches(QKeySequence::Delete)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (_overwriteMode) + replace(posBa, char(0)); + else + remove(posBa, 1); + } + } + + /* Backspace */ + if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (posBa > 0) + { + if (_overwriteMode) + replace(posBa - 1, char(0)); + else + remove(posBa - 1, 1); + setCursorPos(_cursorPosition - 2); + } + } + } + + /* undo */ + if (event->matches(QKeySequence::Undo)) + { + undo(); + } + + /* redo */ + if (event->matches(QKeySequence::Redo)) + { + redo(); + } + + } + + if (event->matches(QKeySequence::Copy)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append('\n'); + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + } + + // Switch between insert/overwrite mode + if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) + { + _overwriteMode = !_overwriteMode; + setCursorPos(_cursorPosition); + overwriteModeChanged(_overwriteMode); + } + + _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); + update(); +} + +void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int actPos = cursorPos(event->pos()); + setCursorPos(actPos); + setSelection(actPos); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int cPos = cursorPos(event->pos()); + resetSelection(cPos); + setCursorPos(cPos); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _xData.size()) + lastLineIdx = _xData.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); + QBrush selected = QBrush(_selectionColor); + QPen colSelected = QPen(Qt::white); + QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); + + painter.setBackgroundMode(Qt::TransparentMode); + + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + int posBa = lineIdx + colIdx; + if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) + { + painter.setBackground(selected); + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setPen(colSelected); + } + else + { + if (_highlighting) + { + // hilight diff bytes + painter.setBackground(highLighted); + if (_xData.dataChanged(posBa)) + { + painter.setPen(colHighlighted); + painter.setBackgroundMode(Qt::OpaqueMode); + } + else + { + painter.setPen(colStandard); + painter.setBackgroundMode(Qt::TransparentMode); + } + } + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + + } + } + painter.setBackgroundMode(Qt::TransparentMode); + painter.setPen(this->palette().color(QPalette::WindowText)); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + int xPosAscii = _xPosAscii; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); + xPosAscii += _charWidth; + } + } + } + + // paint cursor + if (_blink) + { + if (_overwriteMode) + painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); + else + painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); + } + + if (_size != _xData.size()) + { + _size = _xData.size(); + emit currentSizeChanged(_size); + } +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_xData.size() * 2 - 1)) + position = _xData.size() * 2 - 1; + } else { + if (position > (_xData.size() * 2)) + position = _xData.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddressChanged(_cursorPosition/2); +} + +int QHexEditPrivate::cursorPos(QPoint pos) +{ + int result = -1; + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; + result = x + y; + } + return result; +} + +int QHexEditPrivate::cursorPos() +{ + return _cursorPosition; +} + +void QHexEditPrivate::resetSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + _selectionInit = pos; + _selectionBegin = pos; + _selectionEnd = pos; +} + +void QHexEditPrivate::setSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + if (pos >= _selectionInit) + { + _selectionEnd = pos; + _selectionBegin = _selectionInit; + } + else + { + _selectionBegin = pos; + _selectionEnd = _selectionInit; + } +} + +int QHexEditPrivate::getSelectionBegin() +{ + return _selectionBegin; +} + +int QHexEditPrivate::getSelectionEnd() +{ + return _selectionEnd; +} + + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..b802af3 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,120 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include +#include "xbytearray.h" + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setAddressOffset(int offset); + int addressOffset(); + + void setCursorPos(int position); + int cursorPos(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void setReadOnly(bool readOnly); + bool isReadOnly(); + + void setSelectionColor(QColor const &color); + QColor selectionColor(); + + XByteArray & xData(); + + void insert(int index, const QByteArray & ba); + void insert(int index, char ch); + void remove(int index, int len=1); + void replace(int index, char ch); + void replace(int index, const QByteArray & ba); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + + void undo(); + void redo(); + + QString toRedableString(); + QString selectionToReadableString(); + +signals: + void currentAddressChanged(int address); + void currentSizeChanged(int size); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mousePressEvent(QMouseEvent * event); + + void paintEvent(QPaintEvent *event); + + int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION + + void resetSelection(int pos); + void setSelection(int pos); // set min (if below init) or max (if greater init) + int getSelectionBegin(); + int getSelectionEnd(); + + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QColor _highlightingColor; + QColor _selectionColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + QUndoStack *_undoStack; + + XByteArray _xData; // Hält den Inhalt des Hex Editors + + bool _blink; // true: then cursor blinks + bool _renderingRequired; // Flag to store that rendering is necessary + bool _addressArea; // left area of QHexEdit + bool _asciiArea; // medium area + bool _highlighting; // highlighting of changed bytes + bool _overwriteMode; + bool _readOnly; // true: the user can only look and navigate + + int _charWidth, _charHeight; // char dimensions (dpendend on font) + int _cursorX, _cursorY; // graphics position of the cursor + int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) + int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas + + int _selectionBegin; // First selected char + int _selectionEnd; // Last selected char + int _selectionInit; // That's, where we pressed the mouse button + + int _size; +}; + +/** \endcond docNever */ + +#endif + diff --git a/extra/qhexedit2/src/xbytearray.cpp b/extra/qhexedit2/src/xbytearray.cpp new file mode 100644 index 0000000..ec8bf3d --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.cpp @@ -0,0 +1,167 @@ +#include "xbytearray.h" + +XByteArray::XByteArray() +{ + _oldSize = -99; + _addressNumbers = 4; + _addressOffset = 0; + +} + +int XByteArray::addressOffset() +{ + return _addressOffset; +} + +void XByteArray::setAddressOffset(int offset) +{ + _addressOffset = offset; +} + +int XByteArray::addressWidth() +{ + return _addressNumbers; +} + +void XByteArray::setAddressWidth(int width) +{ + if ((width >= 0) and (width<=6)) + { + _addressNumbers = width; + } +} + +QByteArray & XByteArray::data() +{ + return _data; +} + +void XByteArray::setData(QByteArray data) +{ + _data = data; + _changedData = QByteArray(data.length(), char(0)); +} + +bool XByteArray::dataChanged(int i) +{ + return bool(_changedData[i]); +} + +QByteArray XByteArray::dataChanged(int i, int len) +{ + return _changedData.mid(i, len); +} + +void XByteArray::setDataChanged(int i, bool state) +{ + _changedData[i] = char(state); +} + +void XByteArray::setDataChanged(int i, const QByteArray & state) +{ + int length = state.length(); + int len; + if ((i + length) > _changedData.length()) + len = _changedData.length() - i; + else + len = length; + _changedData.replace(i, len, state); +} + +int XByteArray::realAddressNumbers() +{ + if (_oldSize != _data.size()) + { + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + } + return _realAddressNumbers; +} + +int XByteArray::size() +{ + return _data.size(); +} + +QByteArray & XByteArray::insert(int i, char ch) +{ + _data.insert(i, ch); + _changedData.insert(i, char(1)); + return _data; +} + +QByteArray & XByteArray::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _changedData.insert(i, QByteArray(ba.length(), char(1))); + return _data; +} + +QByteArray & XByteArray::remove(int i, int len) +{ + _data.remove(i, len); + _changedData.remove(i, len); + return _data; +} + +QByteArray & XByteArray::replace(int index, char ch) +{ + _data[index] = ch; + _changedData[index] = char(1); + return _data; +} + +QByteArray & XByteArray::replace(int index, const QByteArray & ba) +{ + int len = ba.length(); + return replace(index, len, ba); +} + +QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) +{ + int len; + if ((index + length) > _data.length()) + len = _data.length() - index; + else + len = length; + _data.replace(index, len, ba.mid(0, len)); + _changedData.replace(index, len, QByteArray(len, char(1))); + return _data; +} + +QChar XByteArray::asciiChar(int index) +{ + char ch = _data[index]; + if ((ch < 0x20) or (ch > 0x7e)) + ch = '.'; + return QChar(ch); +} + +QString XByteArray::toRedableString(int start, int end) +{ + int adrWidth = realAddressNumbers(); + if (_addressNumbers > adrWidth) + adrWidth = _addressNumbers; + if (end < 0) + end = _data.size(); + + QString result; + for (int i=start; i < end; i += 16) + { + QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); + QString hexStr; + QString ascStr; + for (int j=0; j<16; j++) + { + if ((i + j) < _data.size()) + { + hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); + ascStr.append(asciiChar(i+j)); + } + } + result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; + } + return result; +} diff --git a/extra/qhexedit2/src/xbytearray.h b/extra/qhexedit2/src/xbytearray.h new file mode 100644 index 0000000..2b67c61 --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.h @@ -0,0 +1,66 @@ +#ifndef XBYTEARRAY_H +#define XBYTEARRAY_H + +/** \cond docNever */ + +#include + +/*! XByteArray represents the content of QHexEcit. +XByteArray comprehend the data itself and informations to store if it was +changed. The QHexEdit component uses these informations to perform nice +rendering of the data + +XByteArray also provides some functionality to insert, replace and remove +single chars and QByteArras. Additionally some functions support rendering +and converting to readable strings. +*/ +class XByteArray +{ +public: + explicit XByteArray(); + + int addressOffset(); + void setAddressOffset(int offset); + + int addressWidth(); + void setAddressWidth(int width); + + QByteArray & data(); + void setData(QByteArray data); + + bool dataChanged(int i); + QByteArray dataChanged(int i, int len); + void setDataChanged(int i, bool state); + void setDataChanged(int i, const QByteArray & state); + + int realAddressNumbers(); + int size(); + + QByteArray & insert(int i, char ch); + QByteArray & insert(int i, const QByteArray & ba); + + QByteArray & remove(int pos, int len); + + QByteArray & replace(int index, char ch); + QByteArray & replace(int index, const QByteArray & ba); + QByteArray & replace(int index, int length, const QByteArray & ba); + + QChar asciiChar(int index); + QString toRedableString(int start=0, int end=-1); + +signals: + +public slots: + +private: + QByteArray _data; + QByteArray _changedData; + + int _addressNumbers; // wanted width of address area + int _addressOffset; // will be added to the real addres inside bytearray + int _realAddressNumbers; // real width of address area (can be greater then wanted width) + int _oldSize; // size of data +}; + +/** \endcond docNever */ +#endif // XBYTEARRAY_H diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..9e8bbea --- /dev/null +++ b/ost.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + common/ostprotogui.pro \ + server/drone.pro \ + client/ostinato.pro diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..30e5130 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,33 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..f1ab422 --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h rpcthread.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp rpcthread.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..7c7789e --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,338 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing method %d since %d is pending; " + "queued message = <%s>", + method->index(), pendingMethodId, req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + + qFatal("exiting"); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, + PB_HDR_SIZE + len, req->DebugString().c_str()); + BUFDUMP(msg, PB_HDR_SIZE); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar msg[PB_HDR_SIZE]; + uchar *p = (uchar*) &msg; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(p+0); + method = qFromBigEndian(p+2); + len = qFromBigEndian(p+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msg, sizeof(msg)); + blob->write((char*)msg, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qDebug("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qDebug("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Parsed as %s", __FUNCTION__, + response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response"); + qDebug("%s", response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method %d <%s>", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg", __FUNCTION__); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..e1fbdf9 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,39 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..fa11cdd --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,72 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { failed = false; blob = NULL; } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr; } + + // Server Side Methods + void SetFailed(const std::string &reason) + { failed = true; errStr = reason; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + QIODevice *blob; + std::string errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..e7affaf --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,57 @@ +/* +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 +*/ + +#include "rpcserver.h" + +#include "rpcthread.h" + +RpcServer::RpcServer() +{ + service = NULL; +} + +RpcServer::~RpcServer() +{ +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + if (!listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + errorString().toAscii().constData()); + return false; + } + + qDebug("The server is running on %s: %d", + serverAddress().toString().toAscii().constData(), + serverPort()); + return true; +} + +void RpcServer::incomingConnection(int socketDescriptor) +{ + RpcThread *thread = new RpcThread(socketDescriptor, service, this); + + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); +} diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..15f7910 --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#if 0 +#include +#include +#include + +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" +#endif + +#include + +// forward declaration +namespace google { + namespace protobuf { + class Service; + } +} + +class RpcServer : public QTcpServer +{ + Q_OBJECT + +#if 0 + QTcpServer *server; + QTcpSocket *clientSock; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; +#endif + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + +protected: + void incomingConnection(int socketDescriptor); + +#if 0 + QString errorString(); + void done(PbRpcController *controller); + +private slots: + void when_newConnection(); + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); +#endif +private: + ::google::protobuf::Service *service; +}; + +#endif diff --git a/rpc/rpcthread.cpp b/rpc/rpcthread.cpp new file mode 100644 index 0000000..81ead4f --- /dev/null +++ b/rpc/rpcthread.cpp @@ -0,0 +1,272 @@ +/* +Copyright (C) 2010, 2014 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 "rpcthread.h" + +#include "pbqtio.h" +#include "pbrpccommon.h" + +#include +#include +#include +#include + +#include "pbrpccontroller.h" // FIXME: move up the order and fix warnings + +#include +#include +#include + +RpcThread::RpcThread( + int socketDescriptor, + ::google::protobuf::Service *service, + QObject *parent) + : QThread(parent), + socketDescriptor(socketDescriptor), + service(service) +{ + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + moveToThread(this); +} + +RpcThread::~RpcThread() +{ +} + +void RpcThread::run() +{ + clientSock = new QTcpSocket; + //qDebug("clientSock = %p, %p", clientSock, this->clientSock); + if (!clientSock->setSocketDescriptor(socketDescriptor)) { + qWarning("Unable to initialize TCP socket for incoming connection"); + return; + } + + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(when_dataAvail()), Qt::DirectConnection); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(when_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(when_error(QAbstractSocket::SocketError))); + + exec(); +} + +QString RpcThread::errorString() +{ + return errorString_; +} + +void RpcThread::done(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + //qDebug("In RpcThread::done"); + + if (controller->Failed()) + { + qDebug("rpc failed"); + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!!"); + qDebug("%s", response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client encoding <%s>", + __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); + //BUFDUMP(msg, len + 8); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + +_exit: + delete controller; + isPending = false; +} + +void RpcThread::when_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + delete inStream; + delete outStream; + + clientSock->deleteLater(); + clientSock = NULL; + + quit(); +} + +void RpcThread::when_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcThread::when_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + + if (!parsing) + { + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + parsing = true; + } + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + goto _error_exit; //! \todo Return Error to client + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + goto _error_exit; //! \todo Return Error to client + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) + req->ParseFromBoundedZeroCopyStream(inStream, len); + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request"); + qDebug("%s", req->InitializationErrorString().c_str()); + qFatal("exiting"); + delete req; + delete resp; + + goto _error_exit2; + } + //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, + //resp->DebugString().c_str()); + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcThread::done, controller)); + + parsing = false; + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("server(%s): discarding msg from client", __FUNCTION__); + return; +} + diff --git a/rpc/rpcthread.h b/rpc/rpcthread.h new file mode 100644 index 0000000..5a9381c --- /dev/null +++ b/rpc/rpcthread.h @@ -0,0 +1,72 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _RPC_THREAD_H +#define _RPC_THREAD_H + +#include +#include + +// forward declarations +class PbRpcController; +class QTcpSocket; +namespace google { + namespace protobuf { + class Service; + namespace io { + class CopyingInputStreamAdaptor; + class CopyingOutputStreamAdaptor; + } + } +} + +class RpcThread : public QThread +{ + Q_OBJECT + +public: + RpcThread(int socketDescriptor, + ::google::protobuf::Service *service, + QObject *parent); + virtual ~RpcThread(); + void run(); + +private: + QString errorString(); // FIXME: needed? why? + void done(PbRpcController *controller); + +private slots: + void when_disconnected(); + void when_dataAvail(); + void when_error(QAbstractSocket::SocketError socketError); + +private: + int socketDescriptor; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + QString errorString_; +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..4aa97c6 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,594 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#define __STDC_FORMAT_MACROS + +#include "abstractport.h" + +#include "../common/streambase.h" +#include "../common/abstractprotocol.h" + +#include +#include + +#include +#include +#include + +AbstractPort::AbstractPort(int id, const char *device) +{ + isUsable_ = true; + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + minPacketSetSize_ = 1; + + maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + if (port.has_transmit_mode()) + data_.set_transmit_mode(port.transmit_mode()); + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::addNote(QString note) +{ + QString notes = QString::fromStdString(data_.notes()); + + note.prepend("
  • "); + note.append("
  • "); + + if (notes.isEmpty()) + notes="Limitation(s)
      "; + else + notes.remove("
    "); + + notes.append(note); + notes.append(""); + + data_.set_notes(notes.toStdString()); +} + +void AbstractPort::updatePacketList() +{ + switch(data_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + updatePacketListSequential(); + break; + case OstProto::kInterleavedTransmit: + updatePacketListInterleaved(); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } +} + +void AbstractPort::updatePacketListSequential() +{ + long sec = 0; + long nsec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + int len; + ulong n, x, y; + ulong burstSize; + double ibg = 0; + quint64 ibg1 = 0, ibg2 = 0; + quint64 nb1 = 0, nb2 = 0; + double ipg = 0; + quint64 ipg1 = 0, ipg2 = 0; + quint64 npx1 = 0, npx2 = 0; + quint64 npy1 = 0, npy2 = 0; + quint64 loopDelay; + ulong frameVariableCount = streamList_[i]->frameVariableCount(); + + // We derive n, x, y such that + // n * x + y = total number of packets to be sent + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + burstSize = streamList_[i]->burstSize(); + x = AbstractProtocol::lcm(frameVariableCount, burstSize); + n = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) / x; + y = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) % x; + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + ibg1 = quint64(ceil(ibg)); + ibg2 = quint64(floor(ibg)); + nb1 = quint64((ibg - double(ibg2)) * double(x)); + nb2 = x - nb1; + } + loopDelay = ibg2; + break; + case OstProto::StreamControl::e_su_packets: + x = frameVariableCount; + n = 2; + while (x < minPacketSetSize_) + x = frameVariableCount*n++; + n = streamList_[i]->numPackets() / x; + y = streamList_[i]->numPackets() % x; + burstSize = x + y; + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + ipg1 = quint64(ceil(ipg)); + ipg2 = quint64(floor(ipg)); + npx1 = quint64((ipg - double(ipg2)) * double(x)); + npx2 = x - npx1; + npy1 = quint64((ipg - double(ipg2)) * double(y)); + npy2 = y - npy1; + } + loopDelay = ipg2; + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + + qDebug("\nframeVariableCount = %lu", frameVariableCount); + qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", + n, x, y, burstSize); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, ibg1); + qDebug("nb1 = %" PRIu64, nb1); + qDebug("ibg2 = %" PRIu64, ibg2); + qDebug("nb2 = %" PRIu64 "\n", nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, ipg1); + qDebug("npx1 = %" PRIu64, npx1); + qDebug("npy1 = %" PRIu64, npy1); + qDebug("ipg2 = %" PRIu64, ipg2); + qDebug("npx2 = %" PRIu64, npx2); + qDebug("npy2 = %" PRIu64 "\n", npy2); + + if (n > 1) + loopNextPacketSet(x, n, 0, loopDelay); + else if (n == 0) + x = 0; + + for (uint j = 0; j < (x+y); j++) + { + + if (j == 0 || frameVariableCount > 1) + { + len = streamList_[i]->frameValue( + pktBuf_, sizeof(pktBuf_), j); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d) sec = %lu nsec = %lu", + i, j, sec, nsec); + + appendToPacketList(sec, nsec, pktBuf_, len); + + if ((j > 0) && (((j+1) % burstSize) == 0)) + { + nsec += (j < nb1) ? ibg1 : ibg2; + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + else + { + if (j < x) + nsec += (j < npx1) ? ipg1 : ipg2; + else + nsec += ((j-x) < npy1) ? ipg1 : ipg2; + + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + } + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, 0, + streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg1 : ipg1); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::updatePacketListInterleaved() +{ + int numStreams = 0; + quint64 minGap = ULLONG_MAX; + quint64 duration = quint64(1e9); + QList ibg1, ibg2; + QList nb1, nb2; + QList ipg1, ipg2; + QList np1, np2; + QList schedSec, schedNsec; + QList pktCount, burstCount; + QList burstSize; + QList isVariable; + QList pktBuf; + QList pktLen; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (!streamList_[i]->isEnabled()) + continue; + + double numBursts = 0; + double numPackets = 0; + + quint64 _burstSize; + double ibg = 0; + quint64 _ibg1 = 0, _ibg2 = 0; + quint64 _nb1 = 0, _nb2 = 0; + double ipg = 0; + quint64 _ipg1 = 0, _ipg2 = 0; + quint64 _np1 = 0, _np2 = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->burstRate(); + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + _ibg1 = quint64(ceil(ibg)); + _ibg2 = quint64(floor(ibg)); + _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); + _nb2 = quint64(numBursts) - _nb1; + _burstSize = streamList_[i]->burstSize(); + } + break; + case OstProto::StreamControl::e_su_packets: + numPackets = streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + _ipg1 = llrint(ceil(ipg)); + _ipg2 = quint64(floor(ipg)); + _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); + _np2 = quint64(numPackets) - _np1; + _burstSize = 1; + } + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, _ibg1); + qDebug("nb1 = %" PRIu64, _nb1); + qDebug("ibg2 = %" PRIu64, _ibg2); + qDebug("nb2 = %" PRIu64 "\n", _nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, _ipg1); + qDebug("np1 = %" PRIu64, _np1); + qDebug("ipg2 = %" PRIu64, _ipg2); + qDebug("np2 = %" PRIu64 "\n", _np2); + + + if (_ibg2 && (_ibg2 < minGap)) + minGap = _ibg2; + + if (_ibg1 && (_ibg1 > duration)) + duration = _ibg1; + + ibg1.append(_ibg1); + ibg2.append(_ibg2); + + nb1.append(_nb1); + nb2.append(_nb1); + + burstSize.append(_burstSize); + + if (_ipg2 && (_ipg2 < minGap)) + minGap = _ipg2; + + if (_np1) + { + if (_ipg1 && (_ipg1 > duration)) + duration = _ipg1; + } + else + { + if (_ipg2 && (_ipg2 > duration)) + duration = _ipg2; + } + + ipg1.append(_ipg1); + ipg2.append(_ipg2); + + np1.append(_np1); + np2.append(_np1); + + schedSec.append(0); + schedNsec.append(0); + + pktCount.append(0); + burstCount.append(0); + + if (streamList_[i]->isFrameVariable()) + { + isVariable.append(true); + pktBuf.append(QByteArray()); + pktLen.append(0); + } + else + { + isVariable.append(false); + pktBuf.append(QByteArray()); + pktBuf.last().resize(kMaxPktSize); + pktLen.append(streamList_[i]->frameValue( + (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); + } + + numStreams++; + } // for i + + qDebug("minGap = %" PRIu64, minGap); + qDebug("duration = %" PRIu64, duration); + + uchar* buf; + int len; + quint64 durSec = duration/ulong(1e9); + quint64 durNsec = duration % ulong(1e9); + quint64 sec = 0; + quint64 nsec = 0; + quint64 lastPktTxSec = 0; + quint64 lastPktTxNsec = 0; + do + { + for (int i = 0; i < numStreams; i++) + { + // If a packet is not scheduled yet, look at the next stream + if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) + continue; + + for (uint j = 0; j < burstSize[i]; j++) + { + if (isVariable.at(i)) + { + buf = pktBuf_; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), + pktCount[i]); + } + else + { + buf = (uchar*) pktBuf.at(i).data(); + len = pktLen.at(i); + } + + if (len <= 0) + continue; + + qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); + appendToPacketList(sec, nsec, buf, len); + lastPktTxSec = sec; + lastPktTxNsec = nsec; + + pktCount[i]++; + schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? + ipg1.at(i) : ipg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + burstCount[i]++; + schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? + ibg1.at(i) : ibg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + nsec += minGap; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } while ((sec < durSec) || (nsec < durNsec)); + + qint64 delaySec = durSec - lastPktTxSec; + qint64 delayNsec = durNsec - lastPktTxNsec; + while (delayNsec < 0) + { + delayNsec += long(1e9); + delaySec--; + } + qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); + setPacketListLoopMode(true, delaySec, delayNsec); + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? + stats_.rxPkts - epochStats_.rxPkts : + stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); + stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? + stats_.rxBytes - epochStats_.rxBytes : + stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? + stats_.txPkts - epochStats_.txPkts : + stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); + stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? + stats_.txBytes - epochStats_.txBytes : + stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; + + stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? + stats_.rxDrops - epochStats_.rxDrops : + stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); + stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? + stats_.rxErrors - epochStats_.rxErrors : + stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); + stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? + stats_.rxFifoErrors - epochStats_.rxFifoErrors : + stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); + stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? + stats_.rxFrameErrors - epochStats_.rxFrameErrors : + stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..44f0c1d --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 rxDrops; + quint64 rxErrors; + quint64 rxFifoErrors; + quint64 rxFrameErrors; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + bool isUsable() { return isUsable_; } + + virtual void init(); + + int id() { return data_.port_id().id(); } + const char* name() { return data_.name().c_str(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) = 0; + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, + quint64 secDelay, quint64 nsecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + void addNote(QString note); + + void updatePacketListSequential(); + void updatePacketListInterleaved(); + + bool isUsable_; + OstProto::Port data_; + OstProto::LinkState linkState_; + ulong minPacketSetSize_; + + quint64 maxStatsValue_; + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/bsdport.cpp b/server/bsdport.cpp new file mode 100644 index 0000000..4ac9ab7 --- /dev/null +++ b/server/bsdport.cpp @@ -0,0 +1,355 @@ +/* +Copyright (C) 2012 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 "bsdport.h" + +#ifdef Q_OS_BSD4 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#define ifr_flagshigh ifr_flags +#define IFF_PPROMISC (IFF_PROMISC << 16) +#endif + +QList BsdPort::allPorts_; +BsdPort::StatsMonitor *BsdPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +BsdPort::BsdPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Bsd + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = ULONG_MAX; +} + +BsdPort::~BsdPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if (ifr.ifr_flagshigh & promisc) + { + ifr.ifr_flagshigh &= ~promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + else + qDebug("Cleared promisc successfully"); + } + else + qDebug("clear_promisc is set but IFF_PPROMISC is not?"); + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void BsdPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +bool BsdPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool BsdPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +BsdPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; +} + +void BsdPort::StatsMonitor::run() +{ + int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + const int mibLen = sizeof(mib)/sizeof(mib[0]); + QHash portStats; + QHash linkState; + int sd; + QByteArray buf; + size_t len; + char *p, *end; + int count; + struct ifreq ifr; + + // + // We first setup stuff before we start polling for stats + // + if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); + return; + } + + qDebug("sysctl mib returns reqd len = %d\n", (int) len); + len *= 2; // for extra room, just in case! + buf.fill('\0', len); + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); + return; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + + // + // Populate the port stats hash table + // + p = buf.data(); + end = p + len; + count = 0; + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); + + if (ifm->ifm_type == RTM_IFINFO) + { + char ifname[1024]; + + strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + + qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); + foreach(BsdPort* port, allPorts_) + { + if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) + { + Q_ASSERT(ifm->ifm_index == sdl->sdl_index); + portStats[uint(ifm->ifm_index)] = &(port->stats_); + linkState[uint(ifm->ifm_index)] = &(port->linkState_); + + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if ((ifr.ifr_flagshigh & promisc) == 0) + { + ifr.ifr_flagshigh |= promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + { + qDebug("%s: set promisc successful", + port->name()); + port->clearPromisc_ = true; + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + else + qDebug("%s: promisc already set", port->name()); + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + count++; + } + p += ifm->ifm_msglen; + } + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in NET_RT_IFLIST - no stats will be available"); + return; + } + + close(sd); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); + goto _try_later; + } + + p = buf.data(); + end = p + len; + + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + AbstractPort::PortStats *stats; + + if (ifm->ifm_type != RTM_IFINFO) + goto _next; + + stats = portStats[ifm->ifm_index]; + if (stats) + { + struct if_data *ifd = &(ifm->ifm_data); + OstProto::LinkState *state = linkState[ifm->ifm_index]; + u_long in_packets; + + Q_ASSERT(state); +#ifdef Q_OS_MAC + *state = ifm->ifm_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; +#else + *state = (OstProto::LinkState) ifd->ifi_link_state; +#endif + + in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxPps = + ((in_packets >= stats->rxPkts) ? + in_packets - stats->rxPkts : + in_packets + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((ifd->ifi_ibytes >= stats->rxBytes) ? + ifd->ifi_ibytes - stats->rxBytes : + ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = in_packets; + stats->rxBytes = ifd->ifi_ibytes; + stats->txPps = + ((ifd->ifi_opackets >= stats->txPkts) ? + ifd->ifi_opackets - stats->txPkts : + ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((ifd->ifi_obytes >= stats->txBytes) ? + ifd->ifi_obytes - stats->txBytes : + ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = ifd->ifi_opackets; + stats->txBytes = ifd->ifi_obytes; + + stats->rxDrops = ifd->ifi_iqdrops; + stats->rxErrors = ifd->ifi_ierrors; + } +_next: + p += ifm->ifm_msglen; + } +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); +} + +void BsdPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/bsdport.h b/server/bsdport.h new file mode 100644 index 0000000..776c39a --- /dev/null +++ b/server/bsdport.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2012 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 +*/ + +#ifndef _SERVER_BSD_PORT_H +#define _SERVER_BSD_PORT_H + +#include + +#ifdef Q_OS_BSD4 + +#include "pcapport.h" + +class BsdPort : public PcapPort +{ +public: + BsdPort(int id, const char *device); + ~BsdPort(); + + void init(); + + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..c46f1df --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,53 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QObject *parent) + : QObject(parent) +{ + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + //qCritical(qPrintable(rpcServer->errorString())); + return false; + } + + return true; +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..9474207 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,40 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QObject +{ + Q_OBJECT +public: + Drone(QObject *parent = 0); + ~Drone(); + bool init(); + +private: + RpcServer *rpcServer; + OstProto::OstService *service; +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..be69521 --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,47 @@ +TEMPLATE = app +CONFIG += qt +QT += network script +QT -= gui +DEFINES += HAVE_REMOTE WPCAP +INCLUDEPATH += "../rpc" +win32 { + CONFIG += console + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lm +LIBS += -lprotobuf +HEADERS += drone.h +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + bsdport.cpp \ + linuxport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..7d8453b --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,83 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" + +#include + +#include + +#ifdef Q_OS_UNIX +#include +#endif + +extern ProtocolManager *OstProtocolManager; + +int myport; + +void cleanup(int /*signum*/) +{ + QCoreApplication::instance()->exit(-1); +} + +int main(int argc, char *argv[]) +{ + int exitCode = 0; + QCoreApplication app(argc, argv); + Drone *drone = new Drone(); + OstProtocolManager = new ProtocolManager(); + + app.setApplicationName(drone->objectName()); + + // TODO: command line options + // -v (--version) + // -h (--help) + // -p (--portnum) + if (argc > 1) + myport = atoi(argv[1]); + + if (!drone->init()) + { + exitCode = -1; + goto _exit; + } + +#ifdef Q_OS_UNIX + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup; + if (sigaction(SIGTERM, &sa, NULL)) + qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); + if (sigaction(SIGINT, &sa, NULL)) + qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); +#endif + + exitCode = app.exec(); + +_exit: + delete drone; + delete OstProtocolManager; + + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + +#include "linuxport.h" + +#ifdef Q_OS_LINUX + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QList LinuxPort::allPorts_; +LinuxPort::StatsMonitor *LinuxPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +LinuxPort::LinuxPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Linux + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = 0xffffffff; +} + +LinuxPort::~LinuxPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if (ifr.ifr_flags & IFF_PROMISC) + { + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + } + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void LinuxPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +OstProto::LinkState LinuxPort::linkState() +{ + return linkState_; +} + +bool LinuxPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool LinuxPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +LinuxPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; + ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(ioctlSocket_ >= 0); +} + +LinuxPort::StatsMonitor::~StatsMonitor() +{ + close(ioctlSocket_); +} + +void LinuxPort::StatsMonitor::run() +{ + if (netlinkStats() < 0) + { + qDebug("netlink stats not available - using /proc stats"); + procStats(); + } +} + +void LinuxPort::StatsMonitor::procStats() +{ + PortStats **portStats; + int fd; + QByteArray buf; + int len; + char *p, *end; + int count, index; + const char* fmtopt[] = { + "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", + }; + const char *fmt; + + // + // We first setup stuff before we start polling for stats + // + fd = open("/proc/net/dev", O_RDONLY); + if (fd < 0) + { + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; + } + + buf.fill('\0', 8192); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + qWarning("initial buffer size is too small. no stats will be available"); + return; + } + + p = buf.data(); + end = p + len; + + // Select scanf format + if (strstr(buf, "compressed")) + fmt = fmtopt[0]; + else + fmt = fmtopt[1]; + + // Count number of lines - number of ports is 2 less than number of lines + count = 0; + while (p < end) + { + if (*p == '\n') + count++; + p++; + } + count -= 2; + + if (count <= 0) + { + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; + } + + portStats = (PortStats**) calloc(count, sizeof(PortStats)); + Q_ASSERT(portStats != NULL); + + // + // Populate the port stats array + // + p = buf.data(); + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + char* q; + + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; + + q = p; + + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) + { + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; + p++; + } + Q_ASSERT(index == count); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; + } + + p = buf.data(); + end = p + len; + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 rxErrors, rxDrops, rxFifo, rxFrame; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, + &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + stats->rxPps = + ((rxPkts >= stats->rxPkts) ? + rxPkts - stats->rxPkts : + rxPkts + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rxBytes >= stats->rxBytes) ? + rxBytes - stats->rxBytes : + rxBytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = + ((txPkts >= stats->txPkts) ? + txPkts - stats->txPkts : + txPkts + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((txBytes >= stats->txBytes) ? + txBytes - stats->txBytes : + txBytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + + stats->rxDrops = rxDrops; + stats->rxErrors = rxErrors; + stats->rxFifoErrors = rxFifo; + stats->rxFrameErrors = rxFrame; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); + } + + free(portStats); +} + +int LinuxPort::StatsMonitor::netlinkStats() +{ + QHash portStats; + QHash linkState; + int fd; + struct sockaddr_nl local; + struct sockaddr_nl kernel; + QByteArray buf; + int len, count; + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtg; + } ifListReq; + struct iovec iov; + struct msghdr msg; + struct nlmsghdr *nlm; + bool done = false; + + // + // We first setup stuff before we start polling for stats + // + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + qWarning("Unable to open netlink socket (errno %d)", errno); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) + { + qWarning("Unable to bind netlink socket (errno %d)", errno); + return -1; + } + + memset(&ifListReq, 0, sizeof(ifListReq)); + ifListReq.nlh.nlmsg_len = sizeof(ifListReq); + ifListReq.nlh.nlmsg_type = RTM_GETLINK; + ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifListReq.nlh.nlmsg_pid = 0; + ifListReq.rtg.rtgen_family = AF_PACKET; + + buf.fill('\0', 1024); + + msg.msg_name = &kernel; + msg.msg_namelen = sizeof(kernel); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); + + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + return -1; + } + + count = 0; + +_retry: + + // Find required size of buffer and resize accordingly + while (1) + { + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + msg.msg_flags = 0; + + // Peek at reply to check buffer size required + len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink closed the socket on my face!"); + return -1; + } + else + { + if (msg.msg_flags & MSG_TRUNC) + { + if (len == buf.size()) // Older Kernel returns truncated size + { + qDebug("netlink buffer size %d not enough", buf.size()); + qDebug("retrying with double the size"); + // Double the size and retry + buf.resize(buf.size()*2); + continue; + } + else // Newer Kernel returns actual size required + { + qDebug("netlink required buffer size = %d", len); + buf.resize(len); + continue; + } + } + else + qDebug("buffer size %d enough for netlink", buf.size()); + + break; + } + } + + msg.msg_flags = 0; + + // Actually receive the reply now + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry; + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + return -1; + } + + // + // Populate the port stats hash table + // + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + char ifname[64] = ""; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error %d", err->error); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == IFLA_IFNAME) + { + strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); + ifname[RTA_PAYLOAD(rta)] = 0; + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + + qDebug("if: %s(%d)", ifname, ifi->ifi_index); + foreach(LinuxPort* port, allPorts_) + { + if (strcmp(port->name(), ifname) == 0) + { + portStats[uint(ifi->ifi_index)] = &(port->stats_); + linkState[uint(ifi->ifi_index)] = &(port->linkState_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + count++; + break; + } + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry; + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); + return - 1; + } + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + goto _try_later; + } + + done = false; + +_retry_recv: + msg.msg_flags = 0; + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry_recv; + qWarning("netlink recv error %d", errno); + break; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + break; + } + + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error: %s", strerror(-err->error)); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + // TODO: IFLA_STATS64 + if (rta->rta_type == IFLA_STATS) + { + struct rtnl_link_stats *rtnlStats = + (struct rtnl_link_stats*) RTA_DATA(rta); + AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + OstProto::LinkState *state = linkState[ifi->ifi_index]; + + if (!stats) + break; + + stats->rxPps = + ((rtnlStats->rx_packets >= stats->rxPkts) ? + rtnlStats->rx_packets - stats->rxPkts : + rtnlStats->rx_packets + (kMaxValue32 + - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rtnlStats->rx_bytes >= stats->rxBytes) ? + rtnlStats->rx_bytes - stats->rxBytes : + rtnlStats->rx_bytes + (kMaxValue32 + - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rtnlStats->rx_packets; + stats->rxBytes = rtnlStats->rx_bytes; + stats->txPps = + ((rtnlStats->tx_packets >= stats->txPkts) ? + rtnlStats->tx_packets - stats->txPkts : + rtnlStats->tx_packets + (kMaxValue32 + - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((rtnlStats->tx_bytes >= stats->txBytes) ? + rtnlStats->tx_bytes - stats->txBytes : + rtnlStats->tx_bytes + (kMaxValue32 + - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = rtnlStats->tx_packets; + stats->txBytes = rtnlStats->tx_bytes; + + // TODO: export detailed error stats + stats->rxDrops = rtnlStats->rx_dropped + + rtnlStats->rx_missed_errors; + stats->rxErrors = rtnlStats->rx_errors; + stats->rxFifoErrors = rtnlStats->rx_fifo_errors; + stats->rxFrameErrors = rtnlStats->rx_crc_errors + + rtnlStats->rx_length_errors + + rtnlStats->rx_over_errors + + rtnlStats->rx_frame_errors; + + Q_ASSERT(state); + *state = ifi->ifi_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; + + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry_recv; + +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); + + return 0; +} + +int LinuxPort::StatsMonitor::setPromisc(const char * portName) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); + + if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) + { + return 1; + } + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + portName, strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + portName, strerror(errno)); + } + + return 0; +} + +void LinuxPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/linuxport.h b/server/linuxport.h new file mode 100644 index 0000000..2658560 --- /dev/null +++ b/server/linuxport.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SERVER_LINUX_PORT_H +#define _SERVER_LINUX_PORT_H + +#include + +#ifdef Q_OS_LINUX + +#include "pcapport.h" + +class LinuxPort : public PcapPort +{ +public: + LinuxPort(int id, const char *device); + ~LinuxPort(); + + void init(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + ~StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + int netlinkStats(); + void procStats(); + int setPromisc(const char* portName); + + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + int ioctlSocket_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/myservice.cpp b/server/myservice.cpp new file mode 100644 index 0000000..be0984a --- /dev/null +++ b/server/myservice.cpp @@ -0,0 +1,475 @@ +/* +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 +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) + portInfo.append(portManager->port(i)); +} + +MyService::~MyService() +{ + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portInfo[id]->protoDataCopyInto(p); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portInfo[id]->modify(port); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::startTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopTransmit(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->startCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->stopCapture(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + + s->set_rx_drops(stats.rxDrops); + s->set_rx_errors(stats.rxErrors); + s->set_rx_fifo_errors(stats.rxFifoErrors); + s->set_rx_frame_errors(stats.rxFrameErrors); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portInfo[portId]->resetStats(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..09cb479 --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include + +#include "../common/protocol.pb.h" + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTx(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + +private: + /*! AbstractPort::id() and index into portInfo[] are same! */ + QList portInfo; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..94ac735 --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,792 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + + +#if defined(Q_OS_LINUX) +typedef struct timeval TimeStamp; +static void inline getTimeStamp(TimeStamp *stamp) +{ + gettimeofday(stamp, NULL); +} + +// Returns time diff in usecs between end and start +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + struct timeval diff; + long usecs; + + timersub(end, start, &diff); + + usecs = diff.tv_usec; + if (diff.tv_sec) + usecs += diff.tv_sec*1e6; + + return usecs; +} +#elif defined(Q_OS_WIN32) +static quint64 gTicksFreq; +typedef LARGE_INTEGER TimeStamp; +static void inline getTimeStamp(TimeStamp* stamp) +{ + QueryPerformanceCounter(stamp); +} + +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + if (end->QuadPart >= start->QuadPart) + return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; + else + { + // FIXME: incorrect! what's the max value for this counter before + // it rolls over? + return (start->QuadPart)*long(1e6)/gTicksFreq; + } +} +#else +typedef int TimeStamp; +static void inline getTimeStamp(TimeStamp*) {} +static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } +#endif + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1 ").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitorRx_) + monitorRx_->stop(); + if (monitorTx_) + monitorTx_->stop(); + + delete capturer_; + delete transmitter_; + + if (monitorRx_) + monitorRx_->wait(); + delete monitorRx_; + + if (monitorTx_) + monitorTx_->wait(); + delete monitorTx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + bool noLocalCapture; + + direction_ = direction; + isDirectional_ = true; + isPromisc_ = true; + noLocalCapture = true; + stats_ = stats; + stop_ = false; + +_retry: +#ifdef Q_OS_WIN32 + int flags = 0; + + if (isPromisc_) + flags |= PCAP_OPENFLAG_PROMISCUOUS; + if (noLocalCapture) + flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; + + handle_ = pcap_open(device, 64 /* FIXME */, flags, + 1000 /* ms */, NULL, errbuf); +#else + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), + 1000 /* ms */, errbuf); +#endif + + if (handle_ == NULL) + { + if (isPromisc_ && QString(errbuf).contains("promiscuous")) + { + qDebug("Can't set promiscuous mode, trying non-promisc %s", device); + isPromisc_ = false; + goto _retry; + } + else if (noLocalCapture && QString(errbuf).contains("loopback")) + { + qDebug("Can't set no local capture mode %s", device); + noLocalCapture = false; + goto _retry; + } + else + goto _open_error; + } +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); +} + +PcapPort::PortMonitor::~PortMonitor() +{ + if (handle_) + pcap_close(handle_); +} + +void PcapPort::PortMonitor::run() +{ + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +void PcapPort::PortMonitor::stop() +{ + stop_ = true; + pcap_breakloop(handle()); +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE] = ""; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + gTicksFreq = ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; + if (usingInternalHandle_) + pcap_close(handle_); +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for packetSequenceList + while(packetSequenceList_.size()) + delete packetSequenceList_.takeFirst(); + + currentPacketSequence_ = NULL; + repeatSequenceStart_ = -1; + repeatSize_ = 0; + packetCount_ = 0; + + returnToQIdx_ = -1; + + setPacketListLoopMode(false, 0, 0); +} + +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) +{ + currentPacketSequence_ = new PacketSequence; + currentPacketSequence_->repeatCount_ = repeats; + currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + + repeatDelayNsec/1000; + + repeatSequenceStart_ = packetSequenceList_.size(); + repeatSize_ = size; + packetCount_ = 0; + + packetSequenceList_.append(currentPacketSequence_); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = nsec/1000; + + if (currentPacketSequence_ == NULL || + !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) + { + if (currentPacketSequence_ != NULL) + { + long usecs; + + usecs = (pktHdr.ts.tv_sec + - currentPacketSequence_->lastPacket_->ts.tv_sec) + * long(1e6); + usecs += (pktHdr.ts.tv_usec + - currentPacketSequence_->lastPacket_->ts.tv_usec); + currentPacketSequence_->usecDelay_ = usecs; + } + + //! \todo (LOW): calculate sendqueue size + currentPacketSequence_ = new PacketSequence; + + packetSequenceList_.append(currentPacketSequence_); + + // Validate that the pkt will fit inside the new currentSendQueue_ + Q_ASSERT(currentPacketSequence_->hasFreeSpace( + sizeof(pcap_pkthdr) + length)); + } + + if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) + { + op = false; + } + + packetCount_++; + if (repeatSize_ > 0 && packetCount_ == repeatSize_) + { + qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", + repeatSequenceStart_, repeatSize_); + + // Set the packetSequence repeatSize + Q_ASSERT(repeatSequenceStart_ >= 0); + Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); + + if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) + { + PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; + + currentPacketSequence_->usecDelay_ = start->usecDelay_; + start->usecDelay_ = 0; + start->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + } + + repeatSize_ = 0; + + // End current pktSeq and trigger a new pktSeq allocation for next pkt + currentPacketSequence_ = NULL; + } + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalHandle_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + long overHead = 0; // overHead should be negative or zero + + qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); + if (packetSequenceList_.size() <= 0) + return; + + for(i = 0; i < packetSequenceList_.size(); i++) { + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, + packetSequenceList_.at(i)->repeatCount_, + packetSequenceList_.at(i)->repeatSize_, + packetSequenceList_.at(i)->usecDelay_); + qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, + packetSequenceList_.at(i)->packets_, + packetSequenceList_.at(i)->usecDuration_); + } + + i = 0; + while (i < packetSequenceList_.size()) + { + +_restart: + int rptSz = packetSequenceList_.at(i)->repeatSize_; + int rptCnt = packetSequenceList_.at(i)->repeatCount_; + + for (int j = 0; j < rptCnt; j++) + { + for (int k = 0; k < rptSz; k++) + { + int ret; + PacketSequence *seq = packetSequenceList_.at(i+k); +#ifdef Q_OS_WIN32 + TimeStamp ovrStart, ovrEnd; + + if (seq->usecDuration_ <= long(1e6)) // 1s + { + getTimeStamp(&ovrStart); + ret = pcap_sendqueue_transmit(handle_, + seq->sendQueue_, kSyncTransmit); + if (ret >= 0) + { + stats_->txPkts += seq->packets_; + stats_->txBytes += seq->bytes_; + + getTimeStamp(&ovrEnd); + overHead += seq->usecDuration_ + - udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + } + if (stop_) + ret = -2; + } + else + { + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); + } +#else + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); +#endif + + if (ret >= 0) + { + long usecs = seq->usecDelay_ + overHead; + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + } + else + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + return; + } + } + } + + // Move to the next Packet Set + i += rptSz; + } + + if (returnToQIdx_ >= 0) + { + long usecs = loopDelay_ + overHead; + + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + + i = returnToQIdx_; + goto _restart; + } +} + +void PcapPort::PortTransmitter::stop() +{ + if (isRunning()) + stop_ = true; +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, long &overHead, int sync) +{ + TimeStamp ovrStart, ovrEnd; + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + getTimeStamp(&ovrStart); + while((char*) hdr < end) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + getTimeStamp(&ovrEnd); + + overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + usec += overHead; + if (usec > 0) + { + udelay(usec); + overHead = 0; + } + else + overHead = usec; + + ts = hdr->ts; + getTimeStamp(&ovrStart); + } + + Q_ASSERT(pktLen > 0); + + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) (pkt + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + if (stop_) + { + return -2; + } + } + + return 0; +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#if defined(Q_OS_WIN32) + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#elif defined(Q_OS_LINUX) + struct timeval delay, target, now; + + //qDebug("usec delay = %ld", usec); + + delay.tv_sec = 0; + delay.tv_usec = usec; + + while (delay.tv_usec >= 1000000) + { + delay.tv_sec++; + delay.tv_usec -= 1000000; + } + + gettimeofday(&now, NULL); + timeradd(&now, &delay, &target); + + do { + gettimeofday(&now, NULL); + } while (timercmp(&now, &target, <)); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + return; + } +_retry: + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + flag, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + return; + } + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; +} + +void PcapPort::PortCapturer::stop() +{ + if (isRunning()) + stop_ = true; +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..d05018b --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,217 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0, 0); + } + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { + transmitter_->loopNextPacketSet(size, repeats, + repeatDelaySec, repeatDelayNsec); + } + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, nsec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) + { + transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + ~PortMonitor(); + void run(); + void stop(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } + protected: + AbstractPort::PortStats *stats_; + bool stop_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + bool isPromisc_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void stop(); + private: + + class PacketSequence + { + public: + PacketSequence() { + sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + lastPacket_ = NULL; + packets_ = 0; + bytes_ = 0; + usecDuration_ = 0; + repeatCount_ = 1; + repeatSize_ = 1; + usecDelay_ = 0; + } + ~PacketSequence() { + pcap_sendqueue_destroy(sendQueue_); + } + bool hasFreeSpace(int size) { + if ((sendQueue_->len + size) <= sendQueue_->maxlen) + return true; + else + return false; + } + int appendPacket(const struct pcap_pkthdr *pktHeader, + const uchar *pktData) { + if (lastPacket_) + { + usecDuration_ += (pktHeader->ts.tv_sec + - lastPacket_->ts.tv_sec) * long(1e6); + usecDuration_ += (pktHeader->ts.tv_usec + - lastPacket_->ts.tv_usec); + } + packets_++; + bytes_ += pktHeader->caplen; + lastPacket_ = (struct pcap_pkthdr *) + (sendQueue_->buffer + sendQueue_->len); + return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); + } + pcap_send_queue *sendQueue_; + struct pcap_pkthdr *lastPacket_; + long packets_; + long bytes_; + ulong usecDuration_; + int repeatCount_; + int repeatSize_; + long usecDelay_; + }; + + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, + int sync); + + quint64 ticksFreq_; + QList packetSequenceList_; + PacketSequence *currentPacketSequence_; + int repeatSequenceStart_; + quint64 repeatSize_; + quint64 packetCount_; + + int returnToQIdx_; + quint64 loopDelay_; + + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void stop(); + QFile* captureFile(); + + private: + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..2981464 --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2010-2012 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 "portmanager.h" + +#include +#include + +#include "bsdport.h" +#include "linuxport.h" +#include "pcapport.h" +#include "winpcapport.h" + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + +#if defined(Q_OS_WIN32) + port = new WinPcapPort(i, device->name); +#elif defined(Q_OS_LINUX) + port = new LinuxPort(i, device->name); +#elif defined(Q_OS_BSD4) + port = new BsdPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + + portList_.append(port); + } + + pcap_freealldevs(deviceList); + + foreach(AbstractPort *port, portList_) + port->init(); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..2407bf2 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..bbd49d8 --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,226 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + monitorRx_->stop(); + monitorTx_->stop(); + monitorRx_->wait(); + monitorTx_->wait(); + + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 256; +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + if (handle()) + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qWarning("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + if (!stop_) + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..d5e8af1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,109 @@ + +#include "ostprotolib.h" +#include "pcapfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" +#include "settings.h" + +#include +#include +#include +#include + +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int /*argc*/, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) +{ + bool isOk; + QString error; + + if (argc != 3) + { + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; + } + + OstProto::StreamConfigList streams; + QString inFile(argv[2]); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) + { + printf("%s: %s\n", + inFile.toAscii().constData(), error.toAscii().constData()); + } + + if (!isOk) + return 1; + + return 0; +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..e8542e4 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,39 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" "../client" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostprotogui -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostprotogui -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostprotogui -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 + +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..718ce69 --- /dev/null +++ b/version.pri @@ -0,0 +1,19 @@ +APP_VERSION = 0.5.1 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ +APP_VERSION_FILE = version.cpp +revtarget.target = $$APP_VERSION_FILE +win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE +unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE +revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + +SOURCES += $$APP_VERSION_FILE +QMAKE_EXTRA_TARGETS += revtarget +POST_TARGETDEPS += $$APP_VERSION_FILE +QMAKE_DISTCLEAN += $$APP_VERSION_FILE From b470ed84bbbec489d6593d7d6f408db2e956743a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 17 May 2014 18:45:02 +0530 Subject: [PATCH 240/294] Reworked the multi-threaded RPC server/connection architecture into a worker-thread arch as reccomended by Qt --- rpc/pbrpc.pro | 4 +- rpc/pbrpccontroller.h | 3 +- rpc/{rpcthread.cpp => rpcconn.cpp} | 74 ++++++++++++++---------------- rpc/{rpcthread.h => rpcconn.h} | 28 ++++++----- rpc/rpcserver.cpp | 27 +++++++++-- rpc/rpcserver.h | 33 ------------- 6 files changed, 75 insertions(+), 94 deletions(-) rename rpc/{rpcthread.cpp => rpcconn.cpp} (82%) rename rpc/{rpcthread.h => rpcconn.h} (72%) diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro index f1ab422..ff28d8a 100644 --- a/rpc/pbrpc.pro +++ b/rpc/pbrpc.pro @@ -3,5 +3,5 @@ CONFIG += qt staticlib QT += network DEFINES += HAVE_REMOTE LIBS += -lprotobuf -HEADERS += rpcserver.h rpcthread.h pbrpccontroller.h pbrpcchannel.h pbqtio.h -SOURCES += rpcserver.cpp rpcthread.cpp pbrpcchannel.cpp +HEADERS += rpcserver.h rpcconn.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp rpcconn.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index fa11cdd..88782e7 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -20,6 +20,7 @@ along with this program. If not, see #ifndef _PB_RPC_CONTROLLER_H #define _PB_RPC_CONTROLLER_H +#include #include class QIODevice; diff --git a/rpc/rpcthread.cpp b/rpc/rpcconn.cpp similarity index 82% rename from rpc/rpcthread.cpp rename to rpc/rpcconn.cpp index 81ead4f..123c25d 100644 --- a/rpc/rpcthread.cpp +++ b/rpc/rpcconn.cpp @@ -17,28 +17,24 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#include "rpcthread.h" +#include "rpcconn.h" #include "pbqtio.h" #include "pbrpccommon.h" +#include "pbrpccontroller.h" #include #include #include #include -#include "pbrpccontroller.h" // FIXME: move up the order and fix warnings - #include #include #include -RpcThread::RpcThread( - int socketDescriptor, - ::google::protobuf::Service *service, - QObject *parent) - : QThread(parent), - socketDescriptor(socketDescriptor), +RpcConnection::RpcConnection(int socketDescriptor, + ::google::protobuf::Service *service) + : socketDescriptor(socketDescriptor), service(service) { inStream = NULL; @@ -46,18 +42,29 @@ RpcThread::RpcThread( isPending = false; pendingMethodId = -1; // don't care as long as isPending is false - - moveToThread(this); } -RpcThread::~RpcThread() +RpcConnection::~RpcConnection() { + qDebug("destroying connection to %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + // If still connected, disconnect + if (clientSock->state() != QAbstractSocket::UnconnectedState) { + clientSock->disconnectFromHost(); + clientSock->waitForDisconnected(); + } + + delete inStream; + delete outStream; + + delete clientSock; } -void RpcThread::run() +void RpcConnection::start() { clientSock = new QTcpSocket; - //qDebug("clientSock = %p, %p", clientSock, this->clientSock); if (!clientSock->setSocketDescriptor(socketDescriptor)) { qWarning("Unable to initialize TCP socket for incoming connection"); return; @@ -67,28 +74,21 @@ void RpcThread::run() clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); inStream = new google::protobuf::io::CopyingInputStreamAdaptor( - new PbQtInputStream(clientSock)); + new PbQtInputStream(clientSock)); inStream->SetOwnsCopyingStream(true); outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( - new PbQtOutputStream(clientSock)); + new PbQtOutputStream(clientSock)); outStream->SetOwnsCopyingStream(true); connect(clientSock, SIGNAL(readyRead()), - this, SLOT(when_dataAvail()), Qt::DirectConnection); + this, SLOT(on_clientSock_dataAvail())); connect(clientSock, SIGNAL(disconnected()), - this, SLOT(when_disconnected())); + this, SLOT(on_clientSock_disconnected())); connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(when_error(QAbstractSocket::SocketError))); - - exec(); + this, SLOT(on_clientSock_error(QAbstractSocket::SocketError))); } -QString RpcThread::errorString() -{ - return errorString_; -} - -void RpcThread::done(PbRpcController *controller) +void RpcConnection::sendRpcReply(PbRpcController *controller) { google::protobuf::Message *response = controller->response(); QIODevice *blob; @@ -96,8 +96,6 @@ void RpcThread::done(PbRpcController *controller) char* const msg = &msgBuf[0]; int len; - //qDebug("In RpcThread::done"); - if (controller->Failed()) { qDebug("rpc failed"); @@ -160,28 +158,23 @@ _exit: isPending = false; } -void RpcThread::when_disconnected() +void RpcConnection::on_clientSock_disconnected() { qDebug("connection closed from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); - delete inStream; - delete outStream; - - clientSock->deleteLater(); - clientSock = NULL; - - quit(); + deleteLater(); + emit closed(); } -void RpcThread::when_error(QAbstractSocket::SocketError socketError) +void RpcConnection::on_clientSock_error(QAbstractSocket::SocketError socketError) { qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), socketError); } -void RpcThread::when_dataAvail() +void RpcConnection::on_clientSock_dataAvail() { uchar msg[PB_HDR_SIZE]; int msgLen; @@ -256,7 +249,8 @@ void RpcThread::when_dataAvail() //qDebug("before service->callmethod()"); service->CallMethod(methodDesc, controller, req, resp, - google::protobuf::NewCallback(this, &RpcThread::done, controller)); + google::protobuf::NewCallback(this, &RpcConnection::sendRpcReply, + controller)); parsing = false; diff --git a/rpc/rpcthread.h b/rpc/rpcconn.h similarity index 72% rename from rpc/rpcthread.h rename to rpc/rpcconn.h index 5a9381c..ce5e5bd 100644 --- a/rpc/rpcthread.h +++ b/rpc/rpcconn.h @@ -17,10 +17,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -#ifndef _RPC_THREAD_H -#define _RPC_THREAD_H +#ifndef _RPC_CONNECTION_H +#define _RPC_CONNECTION_H -#include #include // forward declarations @@ -36,25 +35,25 @@ namespace google { } } -class RpcThread : public QThread +class RpcConnection : public QObject { Q_OBJECT public: - RpcThread(int socketDescriptor, - ::google::protobuf::Service *service, - QObject *parent); - virtual ~RpcThread(); - void run(); + RpcConnection(int socketDescriptor, ::google::protobuf::Service *service); + virtual ~RpcConnection(); private: - QString errorString(); // FIXME: needed? why? - void done(PbRpcController *controller); + void sendRpcReply(PbRpcController *controller); + +signals: + void closed(); private slots: - void when_disconnected(); - void when_dataAvail(); - void when_error(QAbstractSocket::SocketError socketError); + void start(); + void on_clientSock_dataAvail(); + void on_clientSock_error(QAbstractSocket::SocketError socketError); + void on_clientSock_disconnected(); private: int socketDescriptor; @@ -66,7 +65,6 @@ private: bool isPending; int pendingMethodId; - QString errorString_; }; #endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index e7affaf..6aa8f88 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Srivats P. +Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" @@ -19,7 +19,17 @@ along with this program. If not, see #include "rpcserver.h" -#include "rpcthread.h" +#include "rpcconn.h" + +#include + +// FIXME: QThreadX till we change minimum version of Qt from Qt4.3+ to Qt4.4+ +class QThreadX: public QThread +{ +protected: + virtual ~QThreadX() { qDebug("QThreadX going down!"); } + void run() { exec(); } +}; RpcServer::RpcServer() { @@ -50,8 +60,19 @@ bool RpcServer::registerService(::google::protobuf::Service *service, void RpcServer::incomingConnection(int socketDescriptor) { - RpcThread *thread = new RpcThread(socketDescriptor, service, this); + QThread *thread = new QThreadX; // FIXME:QThreadX pending Qt4.4+ + RpcConnection *conn = new RpcConnection(socketDescriptor, service); + conn->moveToThread(thread); + + connect(thread, SIGNAL(started()), conn, SLOT(start())); + + // NOTE: conn "self-destructs" after emitting closed + // use 'closed' to stop execution of the thread + connect(conn, SIGNAL(closed()), thread, SLOT(quit())); + + // setup thread to "self-destruct" when it is done connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); } diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h index 15f7910..0a4acdd 100644 --- a/rpc/rpcserver.h +++ b/rpc/rpcserver.h @@ -20,17 +20,6 @@ along with this program. If not, see #ifndef _RPC_SERVER_H #define _RPC_SERVER_H -#if 0 -#include -#include -#include - -#include - -#include "pbrpccommon.h" -#include "pbrpccontroller.h" -#endif - #include // forward declaration @@ -44,18 +33,6 @@ class RpcServer : public QTcpServer { Q_OBJECT -#if 0 - QTcpServer *server; - QTcpSocket *clientSock; - - ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; - ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; - - bool isPending; - int pendingMethodId; - QString errorString_; -#endif - public: RpcServer(); //! \todo (LOW) use 'parent' param virtual ~RpcServer(); @@ -66,16 +43,6 @@ public: protected: void incomingConnection(int socketDescriptor); -#if 0 - QString errorString(); - void done(PbRpcController *controller); - -private slots: - void when_newConnection(); - void when_disconnected(); - void when_dataAvail(); - void when_error(QAbstractSocket::SocketError socketError); -#endif private: ::google::protobuf::Service *service; }; From f7c3c06845c10ccf38bcfd47b8b8e753a1e082c4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 19 May 2014 18:26:20 +0530 Subject: [PATCH 241/294] Converted some static vars in RpcConnection:on_clientSock_dataAvail() into member vars to avoid contention across connections; fixed condition which causes a portgroup to never query for stats (it assumes a GetStats is already outstanding) caused by a timing condition (repeated connect/disconnect); improved debug output for RPCs on both client and server side --- client/portgroup.cpp | 2 ++ rpc/pbrpcchannel.cpp | 39 +++++++++++++++++++++-------------- rpc/rpcconn.cpp | 46 ++++++++++++++++++++++++++++++++---------- rpc/rpcconn.h | 5 +++++ server/winpcapport.cpp | 2 +- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 9762fac..0d50228 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -138,6 +138,8 @@ void PortGroup::on_rpcChannel_disconnected() emit portListChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId); + isGetStatsPending_ = false; + if (reconnect) { qDebug("starting reconnect timer for %d ms ...", reconnectAfter); diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 7c7789e..5ef1003 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -102,9 +102,10 @@ void PbRpcChannel::CallMethod( if (isPending) { RpcCall call; - qDebug("RpcChannel: queueing method %d since %d is pending; " - "queued message = <%s>", - method->index(), pendingMethodId, req->DebugString().c_str()); + qDebug("RpcChannel: queueing rpc since method %d is pending;<----\n " + "queued method = %d\n" + "queued message = \n%s\n---->", + pendingMethodId, method->index(), req->DebugString().c_str()); call.method = method; call.controller = controller; @@ -122,10 +123,9 @@ void PbRpcChannel::CallMethod( if (!req->IsInitialized()) { - qWarning("RpcChannel: missing required fields in request"); - qDebug("%s", req->InitializationErrorString().c_str()); - - qFatal("exiting"); + qWarning("RpcChannel: missing required fields in request <----"); + qDebug("req = \n%s", req->DebugString().c_str()); + qDebug("error = \n%s\n--->", req->InitializationErrorString().c_str()); controller->SetFailed("Required fields missing"); done->Run(); @@ -146,9 +146,11 @@ void PbRpcChannel::CallMethod( // Avoid printing stats since it happens every couple of seconds if (pendingMethodId != 13) { - qDebug("client(%s) sending %d bytes encoding <%s>", __FUNCTION__, - PB_HDR_SIZE + len, req->DebugString().c_str()); + qDebug("client(%s) sending %d bytes <----", __FUNCTION__, + PB_HDR_SIZE + len); BUFDUMP(msg, PB_HDR_SIZE); + qDebug("method = %d\n req = \n%s\n---->", + method->index(), req->DebugString().c_str()); } mpSocket->write(msg, PB_HDR_SIZE); @@ -256,14 +258,17 @@ void PbRpcChannel::on_mpSocket_readyRead() // Avoid printing stats if (method != 13) { - qDebug("client(%s): Parsed as %s", __FUNCTION__, - response->DebugString().c_str()); + qDebug("client(%s): Received Msg <---- ", __FUNCTION__); + qDebug("method = %d\nresp = \n%s\n---->", + method, response->DebugString().c_str()); } if (!response->IsInitialized()) { - qWarning("RpcChannel: missing required fields in response"); - qDebug("%s", response->InitializationErrorString().c_str()); + qWarning("RpcChannel: missing required fields in response <----"); + qDebug("resp = \n%s", response->DebugString().c_str()); + qDebug("error = \n%s\n--->", + response->InitializationErrorString().c_str()); controller->SetFailed("Required fields missing"); } @@ -286,7 +291,9 @@ void PbRpcChannel::on_mpSocket_readyRead() if (pendingCallList.size()) { RpcCall call = pendingCallList.takeFirst(); - qDebug("RpcChannel: executing queued method %d <%s>", + qDebug("RpcChannel: executing queued method <----\n" + "method = %d\n" + "req = \n%s\n---->", call.method->index(), call.request->DebugString().c_str()); CallMethod(call.method, call.controller, call.request, call.response, call.done); @@ -298,7 +305,9 @@ _error_exit: inStream->Skip(len); _error_exit2: parsing = false; - qDebug("client(%s) discarding received msg", __FUNCTION__); + qDebug("client(%s) discarding received msg <----", __FUNCTION__); + qDebug("method = %d\nreq = \n%s\n---->", + method, response->DebugString().c_str()); return; } diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 123c25d..0e162a0 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -42,6 +42,8 @@ RpcConnection::RpcConnection(int socketDescriptor, isPending = false; pendingMethodId = -1; // don't care as long as isPending is false + + parsing = false; } RpcConnection::~RpcConnection() @@ -69,6 +71,7 @@ void RpcConnection::start() qWarning("Unable to initialize TCP socket for incoming connection"); return; } + qDebug("clientSock Thread = %p", clientSock->thread()); qDebug("accepting new connection from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), @@ -129,8 +132,11 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) if (!response->IsInitialized()) { - qWarning("response missing required fields!!"); - qDebug("%s", response->InitializationErrorString().c_str()); + qWarning("response missing required fields!! <----"); + qDebug("response = \n%s" + "missing = \n%s---->", + response->DebugString().c_str(), + response->InitializationErrorString().c_str()); qFatal("exiting"); goto _exit; } @@ -144,9 +150,11 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 13) { - qDebug("Server(%s): sending %d bytes to client encoding <%s>", - __FUNCTION__, len + PB_HDR_SIZE, response->DebugString().c_str()); - //BUFDUMP(msg, len + 8); + qDebug("Server(%s): sending %d bytes to client <----", + __FUNCTION__, len + PB_HDR_SIZE); + BUFDUMP(msg, len + 8); + qDebug("method = %d\nreq = \n%s---->", + pendingMethodId, response->DebugString().c_str()); } clientSock->write(msg, PB_HDR_SIZE); @@ -178,9 +186,11 @@ void RpcConnection::on_clientSock_dataAvail() { uchar msg[PB_HDR_SIZE]; int msgLen; +#if 0 static bool parsing = false; static quint16 type, method; static quint32 len; +#endif const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; PbRpcController *controller; @@ -228,21 +238,35 @@ void RpcConnection::on_clientSock_dataAvail() req = service->GetRequestPrototype(methodDesc).New(); resp = service->GetResponsePrototype(methodDesc).New(); - if (len) - req->ParseFromBoundedZeroCopyStream(inStream, len); + if (len) { + bool ok = req->ParseFromBoundedZeroCopyStream(inStream, len); + if (!ok) + qWarning("ParseFromBoundedZeroCopyStream fail " + "for method %d and len %d", method, len); + } if (!req->IsInitialized()) { - qWarning("Missing required fields in request"); - qDebug("%s", req->InitializationErrorString().c_str()); + qWarning("Missing required fields in request <----"); + qDebug("method = %d\n" + "req = \n%s" + "missing = \n%s----->", + method, req->DebugString().c_str(), + req->InitializationErrorString().c_str()); qFatal("exiting"); delete req; delete resp; goto _error_exit2; } - //qDebug("Server(%s): successfully parsed as <%s>", __FUNCTION__, - //resp->DebugString().c_str()); + + if (method != 13) { + qDebug("Server(%s): successfully received/parsed msg <----", __FUNCTION__); + qDebug("method = %d\n" + "req = \n%s---->", + method, + req->DebugString().c_str()); + } controller = new PbRpcController(req, resp); diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h index ce5e5bd..6d19d12 100644 --- a/rpc/rpcconn.h +++ b/rpc/rpcconn.h @@ -65,6 +65,11 @@ private: bool isPending; int pendingMethodId; + + bool parsing; + quint16 type; + quint16 method; + quint32 len; }; #endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp index bbd49d8..f42adaa 100644 --- a/server/winpcapport.cpp +++ b/server/winpcapport.cpp @@ -140,7 +140,7 @@ void WinPcapPort::PortMonitor::run() quint64 lastTxPkts = 0; quint64 lastTxBytes = 0; - qWarning("in %s", __PRETTY_FUNCTION__); + qDebug("in %s", __PRETTY_FUNCTION__); lastTs.tv_sec = 0; lastTs.tv_usec = 0; From 87982a4227d851dcb19192417204a052792a6d4c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 21 May 2014 05:35:04 +0530 Subject: [PATCH 242/294] RpcConnection now uses the common idiom of peekHdr-getMsgLen-waitForLenBytes-readMsg for reading RPC messages from client --- rpc/rpcconn.cpp | 51 +++++++++++++++++++++++++------------------------ rpc/rpcconn.h | 5 ----- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 0e162a0..6f47359 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -42,8 +42,6 @@ RpcConnection::RpcConnection(int socketDescriptor, isPending = false; pendingMethodId = -1; // don't care as long as isPending is false - - parsing = false; } RpcConnection::~RpcConnection() @@ -185,33 +183,39 @@ void RpcConnection::on_clientSock_error(QAbstractSocket::SocketError socketError void RpcConnection::on_clientSock_dataAvail() { uchar msg[PB_HDR_SIZE]; - int msgLen; -#if 0 - static bool parsing = false; - static quint16 type, method; - static quint32 len; -#endif + int msgLen; + quint16 type, method; + quint32 len; const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; PbRpcController *controller; - if (!parsing) - { - if (clientSock->bytesAvailable() < PB_HDR_SIZE) - return; + // Do we have enough bytes for a msg header? + // If yes, peek into the header and get msg length + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; - msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); - - Q_ASSERT(msgLen == PB_HDR_SIZE); - - type = qFromBigEndian(&msg[0]); - method = qFromBigEndian(&msg[2]); - len = qFromBigEndian(&msg[4]); - //qDebug("type = %d, method = %d, len = %d", type, method, len); - - parsing = true; + msgLen = clientSock->peek((char*)msg, PB_HDR_SIZE); + if (msgLen != PB_HDR_SIZE) { + qWarning("asked to peek %d bytes, was given only %d bytes", + PB_HDR_SIZE, msgLen); + return; } + len = qFromBigEndian(&msg[4]); + + // Is the full msg available to read? If not, wait till such time + if (clientSock->bytesAvailable() < (PB_HDR_SIZE+len)) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + if (type != PB_MSG_TYPE_REQUEST) { qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, @@ -276,14 +280,11 @@ void RpcConnection::on_clientSock_dataAvail() google::protobuf::NewCallback(this, &RpcConnection::sendRpcReply, controller)); - parsing = false; - return; _error_exit: inStream->Skip(len); _error_exit2: - parsing = false; qDebug("server(%s): discarding msg from client", __FUNCTION__); return; } diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h index 6d19d12..ce5e5bd 100644 --- a/rpc/rpcconn.h +++ b/rpc/rpcconn.h @@ -65,11 +65,6 @@ private: bool isPending; int pendingMethodId; - - bool parsing; - quint16 type; - quint16 method; - quint32 len; }; #endif From 8e14e0c15b25d107e6a57ef36f74559f89010e81 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 22 May 2014 06:20:55 +0530 Subject: [PATCH 243/294] Prepended qDebug output with connection id for easier debugging --- rpc/rpcconn.cpp | 26 +++++++++++++++++++++++++- rpc/rpcconn.h | 1 + rpc/rpcserver.cpp | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 6f47359..5d9ac4d 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -29,9 +29,17 @@ along with this program. If not, see #include #include +#include #include +#include +#include #include +#include +#include + +static QThreadStorage connId; + RpcConnection::RpcConnection(int socketDescriptor, ::google::protobuf::Service *service) : socketDescriptor(socketDescriptor), @@ -64,6 +72,7 @@ RpcConnection::~RpcConnection() void RpcConnection::start() { + QString id = QString("[%1:%2] "); clientSock = new QTcpSocket; if (!clientSock->setSocketDescriptor(socketDescriptor)) { qWarning("Unable to initialize TCP socket for incoming connection"); @@ -71,6 +80,9 @@ void RpcConnection::start() } qDebug("clientSock Thread = %p", clientSock->thread()); + connId.setLocalData(new QString(id.arg(clientSock->peerAddress().toString()) + .arg(clientSock->peerPort()))); + qDebug("accepting new connection from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); @@ -150,7 +162,7 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) { qDebug("Server(%s): sending %d bytes to client <----", __FUNCTION__, len + PB_HDR_SIZE); - BUFDUMP(msg, len + 8); + BUFDUMP(msg, 8); qDebug("method = %d\nreq = \n%s---->", pendingMethodId, response->DebugString().c_str()); } @@ -289,3 +301,15 @@ _error_exit2: return; } +void RpcConnection::connIdMsgHandler(QtMsgType type, const char* msg) +{ + if (connId.hasLocalData()) { + QString newMsg(*connId.localData()); + newMsg.append(msg); + newMsg.replace(QChar('\n'), QString("\n").append(*connId.localData())); + msg = qPrintable(newMsg); + } + + fprintf(stderr, "%s\n", msg); + fflush(stderr); +} diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h index ce5e5bd..fb23bb8 100644 --- a/rpc/rpcconn.h +++ b/rpc/rpcconn.h @@ -42,6 +42,7 @@ class RpcConnection : public QObject public: RpcConnection(int socketDescriptor, ::google::protobuf::Service *service); virtual ~RpcConnection(); + static void connIdMsgHandler(QtMsgType type, const char* msg); private: void sendRpcReply(PbRpcController *controller); diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp index 6aa8f88..dad4b76 100644 --- a/rpc/rpcserver.cpp +++ b/rpc/rpcserver.cpp @@ -34,6 +34,8 @@ protected: RpcServer::RpcServer() { service = NULL; + + qInstallMsgHandler(RpcConnection::connIdMsgHandler); } RpcServer::~RpcServer() From 0ebff976d805bda8cdf16962f87e2ab03c762f4f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 22 May 2014 20:24:04 +0530 Subject: [PATCH 244/294] Fixed build warnings --- common/abstractprotocolconfig.h | 4 ++-- server/abstractport.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h index 3e87aeb..f9fd971 100644 --- a/common/abstractprotocolconfig.h +++ b/common/abstractprotocolconfig.h @@ -69,7 +69,7 @@ public: Subclasses MUST implement this function. See the SampleProtocol for an example */ - virtual void loadWidget(AbstractProtocol *proto) + virtual void loadWidget(AbstractProtocol* /*proto*/) { // Do nothing! } @@ -83,7 +83,7 @@ public: Subclasses MUST implement this function. See the SampleProtocol for an example */ - virtual void storeWidget(AbstractProtocol *proto) + virtual void storeWidget(AbstractProtocol* /*proto*/) { // Do nothing! } diff --git a/server/abstractport.cpp b/server/abstractport.cpp index 4aa97c6..ef3e881 100644 --- a/server/abstractport.cpp +++ b/server/abstractport.cpp @@ -172,7 +172,7 @@ void AbstractPort::updatePacketListSequential() { if (streamList_[i]->isEnabled()) { - int len; + int len = 0; ulong n, x, y; ulong burstSize; double ibg = 0; @@ -366,7 +366,7 @@ void AbstractPort::updatePacketListInterleaved() double numBursts = 0; double numPackets = 0; - quint64 _burstSize; + quint64 _burstSize = 0; double ibg = 0; quint64 _ibg1 = 0, _ibg2 = 0; quint64 _nb1 = 0, _nb2 = 0; From 8336d77dfaee9686191e158d33be5d612e172f4d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 22 May 2014 20:37:53 +0530 Subject: [PATCH 245/294] Added ReadWriteLock to protect RPC service methods in a multi-threaded environment --- server/myservice.cpp | 44 ++++++++++++++++++++++++++++++++++++++++---- server/myservice.h | 17 ++++++++++++++--- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/server/myservice.cpp b/server/myservice.cpp index be0984a..5a591aa 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -38,12 +38,16 @@ MyService::MyService() PortManager *portManager = PortManager::instance(); int n = portManager->portCount(); - for (int i = 0; i < n; i++) + for (int i = 0; i < n; i++) { portInfo.append(portManager->port(i)); + portLock.append(new QReadWriteLock()); + } } MyService::~MyService() { + while (!portLock.isEmpty()) + delete portLock.takeFirst(); //! \todo Use a singleton destroyer instead // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt delete PortManager::instance(); @@ -56,6 +60,9 @@ void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, { qDebug("In %s", __PRETTY_FUNCTION__); + // No locks are needed here because the list does not change + // and neither does the port_id + for (int i = 0; i < portInfo.size(); i++) { ::OstProto::PortId *p; @@ -84,7 +91,9 @@ void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, OstProto::Port *p; p = response->add_port(); + portLock[id]->lockForRead(); portInfo[id]->protoDataCopyInto(p); + portLock[id]->unlock(); } } @@ -107,7 +116,9 @@ void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, id = port.port_id().id(); if (id < portInfo.size()) { + portLock[id]->lockForWrite(); portInfo[id]->modify(port); + portLock[id]->unlock(); } } @@ -129,6 +140,7 @@ void MyService::getStreamIdList(::google::protobuf::RpcController* controller, goto _invalid_port; response->mutable_port_id()->set_id(portId); + portLock[portId]->lockForRead(); for (int i = 0; i < portInfo[portId]->streamCount(); i++) { OstProto::StreamId *s; @@ -136,11 +148,13 @@ void MyService::getStreamIdList(::google::protobuf::RpcController* controller, s = response->add_stream_id(); s->set_id(portInfo[portId]->streamAtIndex(i)->id()); } + portLock[portId]->unlock(); + done->Run(); return; _invalid_port: - controller->SetFailed("Invalid Port Id"); + controller->SetFailed("Invalid Port Id"); done->Run(); } @@ -158,9 +172,10 @@ void MyService::getStreamConfig(::google::protobuf::RpcController* controller, goto _invalid_port; response->mutable_port_id()->set_id(portId); + portLock[portId]->lockForRead(); for (int i = 0; i < request->stream_id_size(); i++) { - StreamBase *stream; + StreamBase *stream; OstProto::Stream *s; stream = portInfo[portId]->stream(request->stream_id(i).id()); @@ -170,6 +185,8 @@ void MyService::getStreamConfig(::google::protobuf::RpcController* controller, s = response->add_stream(); stream->protoDataCopyInto(*s); } + portLock[portId]->unlock(); + done->Run(); return; @@ -191,6 +208,7 @@ void MyService::addStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) { StreamBase *stream; @@ -206,8 +224,8 @@ void MyService::addStream(::google::protobuf::RpcController* controller, stream = new StreamBase; stream->setId(request->stream_id(i).id()); portInfo[portId]->addStream(stream); - } + portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? @@ -232,8 +250,10 @@ void MyService::deleteStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) portInfo[portId]->deleteStream(request->stream_id(i).id()); + portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? @@ -258,6 +278,7 @@ void MyService::modifyStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_size(); i++) { StreamBase *stream; @@ -272,6 +293,7 @@ void MyService::modifyStream(::google::protobuf::RpcController* controller, if (portInfo[portId]->isDirty()) portInfo[portId]->updatePacketList(); + portLock[portId]->unlock(); //! \todo(LOW): fill-in response "Ack"???? @@ -298,7 +320,9 @@ void MyService::startTx(::google::protobuf::RpcController* /*controller*/, if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? + portLock[portId]->lockForWrite(); portInfo[portId]->startTransmit(); + portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? @@ -321,7 +345,9 @@ void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? + portLock[portId]->lockForWrite(); portInfo[portId]->stopTransmit(); + portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? @@ -344,7 +370,9 @@ void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? + portLock[portId]->lockForWrite(); portInfo[portId]->startCapture(); + portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? @@ -366,7 +394,9 @@ void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? + portLock[portId]->lockForWrite(); portInfo[portId]->stopCapture(); + portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? @@ -387,9 +417,11 @@ void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + portLock[portId]->lockForWrite(); portInfo[portId]->stopCapture(); static_cast(controller)->setBinaryBlob( portInfo[portId]->captureData()); + portLock[portId]->unlock(); done->Run(); return; @@ -421,11 +453,13 @@ void MyService::getStats(::google::protobuf::RpcController* /*controller*/, s->mutable_port_id()->set_id(request->port_id(i).id()); st = s->mutable_state(); + portLock[portId]->lockForRead(); st->set_link_state(portInfo[portId]->linkState()); st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); st->set_is_capture_on(portInfo[portId]->isCaptureOn()); portInfo[portId]->stats(&stats); + portLock[portId]->unlock(); #if 0 if (portId == 2) @@ -466,7 +500,9 @@ void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? + portLock[portId]->lockForWrite(); portInfo[portId]->resetStats(); + portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? diff --git a/server/myservice.h b/server/myservice.h index 09cb479..3508dc4 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -20,10 +20,11 @@ along with this program. If not, see #ifndef _MY_SERVICE_H #define _MY_SERVICE_H -#include - #include "../common/protocol.pb.h" +#include +#include + #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 @@ -98,8 +99,18 @@ public: ::google::protobuf::Closure* done); private: - /*! AbstractPort::id() and index into portInfo[] are same! */ + /* + * NOTES: + * - AbstractPort::id() and index into portInfo[] are same! + * - portLock[] size and order should be same as portInfo[] as the + * same index is used for both. + * - we assume that once populated by the constructor, the list(s) + * never change (objects in the list can change, but not the list itself) + * - locking is at port granularity, not at stream granularity - for now + * this seems sufficient. Revisit later, if required + */ QList portInfo; + QList portLock; }; From 35472a6ac781ec4b0190672c1f67eeaad7590864 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 3 Jun 2014 07:14:54 +0530 Subject: [PATCH 246/294] CLI: Initial Commit for Python Bindings --- .hgignore | 2 + binding/README.txt | 0 binding/__init__.py | 17 ++++++ binding/core.py | 48 +++++++++++++++ binding/example.py | 111 ++++++++++++++++++++++++++++++++++ binding/protocols/__init__.py | 17 ++++++ binding/rpc.py | 63 +++++++++++++++++++ binding/setup.py | 42 +++++++++++++ common/ostproto.pro | 3 + common/protocol.proto | 1 + protobuf.pri | 11 +++- 11 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 binding/README.txt create mode 100644 binding/__init__.py create mode 100644 binding/core.py create mode 100644 binding/example.py create mode 100644 binding/protocols/__init__.py create mode 100644 binding/rpc.py create mode 100644 binding/setup.py diff --git a/.hgignore b/.hgignore index cd5490f..7e37eb9 100644 --- a/.hgignore +++ b/.hgignore @@ -1,6 +1,7 @@ syntax: glob # generated object files +*.pyc *.o *.a *.exe @@ -20,6 +21,7 @@ Makefile* # protobuf generated files *.pb.h *.pb.cc +*_pb2.py # ostinato generated files version.cpp diff --git a/binding/README.txt b/binding/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/binding/__init__.py b/binding/__init__.py new file mode 100644 index 0000000..161ae68 --- /dev/null +++ b/binding/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2014 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 + diff --git a/binding/core.py b/binding/core.py new file mode 100644 index 0000000..a25d965 --- /dev/null +++ b/binding/core.py @@ -0,0 +1,48 @@ +# Copyright (C) 2014 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 + +from rpc import OstinatoRpcChannel, OstinatoRpcController +import protocols.protocol_pb2 as ost_pb + +class DroneProxy(object): + + def __init__(self, host_name, port_number=7878): + self.host = host_name + self.port = port_number + self.channel = OstinatoRpcChannel() + self.stub = ost_pb.OstService_Stub(self.channel) + + for method in self.stub.GetDescriptor().methods: + fn = lambda request, method_name=method.name: \ + self.callRpcMethod(method_name, request) + self.__dict__[method.name] = fn + + def hostName(self): + return self.host + + def portNumber(self): + return self.port + + def connect(self): + self.channel.connect(self.host, self.port) + + def callRpcMethod(self, method_name, request): + controller = OstinatoRpcController() + ost_pb.OstService_Stub.__dict__[method_name]( + self.stub, controller, request, None) + return controller.response + diff --git a/binding/example.py b/binding/example.py new file mode 100644 index 0000000..54d13ef --- /dev/null +++ b/binding/example.py @@ -0,0 +1,111 @@ +# standard modules +import sys +import time +import logging + +# ostinato modules - prepend 'ostinato.' to the module names when using +# an installed package i.e ostinato.core and ostinato.protocols.xxx +from core import ost_pb, DroneProxy +from protocols.mac_pb2 import mac +from protocols.ip4_pb2 import ip4, Ip4 + +host_name = '127.0.0.1' +tx_port_number = 1 +rx_port_number = 1 + +# setup logging +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.DEBUG) + +drone = DroneProxy(host_name) + +try: + # connect to drone + log.info('connecting to drone(%s:%d)' + % (drone.hostName(), drone.portNumber())) + drone.connect() + + tx_port = ost_pb.PortIdList() + tx_port.port_id.add().id = tx_port_number; + + rx_port = ost_pb.PortIdList() + rx_port.port_id.add().id = rx_port_number; + + # verify tx and rx ports exist + log.info('verifying tx_port %d' % tx_port.port_id[0].id) + port_config_list = drone.getPortConfig(tx_port) + log.info('-->' + port_config_list.__str__()) + if len(port_config_list.port) <= 0: + log.error('invalid tx_port' + + tx_port_number) + sys.exit(1) + + log.info('verifying rx_port %d' % rx_port.port_id[0].id) + port_config_list = drone.getPortConfig(rx_port) + log.info('-->' + port_config_list.__str__()) + if len(port_config_list.port) <= 0: + log.error('invalid rx_port' + + rx_port_number) + sys.exit(1) + + # add a stream + stream_id = ost_pb.StreamIdList() + stream_id.port_id.CopyFrom(tx_port.port_id[0]) + stream_id.stream_id.add().id = 1 + log.info('adding tx_stream %d' % stream_id.stream_id[0].id) + drone.addStream(stream_id) + + # configure the stream + stream_cfg = ost_pb.StreamConfigList() + stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) + s = stream_cfg.stream.add() + s.stream_id.id = stream_id.stream_id[0].id + s.core.is_enabled = 1 + s.control.num_packets = 5 + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber + p.Extensions[mac].dst_mac = 0x001122334455 + p.Extensions[mac].src_mac = 0x00aabbccddee + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber + p.Extensions[ip4].src_ip = 0x01020304 + p.Extensions[ip4].dst_ip = 0x05060708 + p.Extensions[ip4].dst_ip_mode = Ip4.e_im_inc_host + + s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + + log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) + drone.modifyStream(stream_cfg) + + # clear tx/rx stats + log.info('clearing tx/rx stats') + drone.clearStats(tx_port) + drone.clearStats(rx_port) + + # start transmit + log.info('starting transmit') + drone.startTx(tx_port) + + # wait for transmit to finish + log.info('waiting for transmit to finish ...') + time.sleep(7) + + # get tx/rx stats + log.info('retreiving stats') + tx_stats = drone.getStats(tx_port) + rx_stats = drone.getStats(rx_port) + + log.info('--> (tx_stats)' + tx_stats.__str__()) + log.info('--> (rx_stats)' + rx_stats.__str__()) + log.info('tx pkts = %d, rx pkts = %d' % + (tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts)) + +except Exception, ex: + log.exception(ex) + sys.exit(1) diff --git a/binding/protocols/__init__.py b/binding/protocols/__init__.py new file mode 100644 index 0000000..161ae68 --- /dev/null +++ b/binding/protocols/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2014 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 + diff --git a/binding/rpc.py b/binding/rpc.py new file mode 100644 index 0000000..6da362f --- /dev/null +++ b/binding/rpc.py @@ -0,0 +1,63 @@ +# Copyright (C) 2014 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 + +from google.protobuf.service import RpcChannel +from google.protobuf.service import RpcController +import socket +import struct + +class OstinatoRpcController(RpcController): + def __init__(self): + super(OstinatoRpcController, self).__init__() + +class OstinatoRpcChannel(RpcChannel): + def __init__(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def connect(self, host, port): + self.sock.connect((host, port)) + + def CallMethod(self, method, controller, request, response_class, done): + OST_PB_MSG_HDR_SIZE = 8 + OST_PB_MSG_TYPE_REQUEST = 1 + req = request.SerializeToString() + self.sock.sendall(struct.pack('>HHI', + OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) + + hdr = '' + while len(hdr) < OST_PB_MSG_HDR_SIZE: + chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) + if chunk == '': + raise RuntimeError("socket connection broken") + hdr = hdr + chunk + + (type, method, resp_len) = struct.unpack('>HHI', hdr) + + resp = '' + while len(resp) < resp_len: + chunk = self.sock.recv(resp_len - len(resp)) + if chunk == '': + raise RuntimeError("socket connection broken") + resp = resp + chunk + + response = response_class() + response.ParseFromString(resp) + + controller.response = response + + + diff --git a/binding/setup.py b/binding/setup.py new file mode 100644 index 0000000..a36face --- /dev/null +++ b/binding/setup.py @@ -0,0 +1,42 @@ +# Copyright (C) 2014 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 + +import os +import shutil +import sys +from setuptools import setup + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +if sys.argv[1] == 'clean_sdist': + shutil.rmtree('dist', ignore_errors = True) + shutil.rmtree('ostinato.egg-info', ignore_errors = True) + sys.exit(0) + +setup(name = 'ostinato', + version = 'FIXME', + author = 'Srivats P', + author_email = 'pstavirs@gmail.com', + license = "GPLv3+", + url = 'http://ostinato.org', + description = 'Ostinato is a network packet and traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. It features custom packet crafting via a GUI or a script', + long_description = read('README.txt'), + install_requires = ['google.protobuf>=2.3'], + packages=['ostinato', 'ostinato.protocols'], + package_dir={'ostinato': ''} + ) diff --git a/common/ostproto.pro b/common/ostproto.pro index 05024e4..e4467dc 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -107,5 +107,8 @@ SOURCES += \ QMAKE_DISTCLEAN += object_script.* +#binding.depends = compiler_protobuf_py_make_all +#QMAKE_EXTRA_TARGETS += binding + include(../protobuf.pri) diff --git a/common/protocol.proto b/common/protocol.proto index 9a74654..07012b5 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -19,6 +19,7 @@ along with this program. If not, see package OstProto; option cc_generic_services = true; +option py_generic_services = true; message StreamId { required uint32 id = 1; diff --git a/protobuf.pri b/protobuf.pri index 30e5130..6316a38 100644 --- a/protobuf.pri +++ b/protobuf.pri @@ -20,7 +20,8 @@ for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} protobuf_decl.name = protobuf header protobuf_decl.input = PROTOS protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h -protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +#protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.commands = protoc --cpp_out="." --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME} protobuf_decl.variable_out = GENERATED_FILES QMAKE_EXTRA_COMPILERS += protobuf_decl @@ -31,3 +32,11 @@ protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h protobuf_impl.commands = $$escape_expand(\n) protobuf_impl.variable_out = GENERATED_SOURCES QMAKE_EXTRA_COMPILERS += protobuf_impl + +protobuf_py.name = protobuf python binding +protobuf_py.input = PROTOS +protobuf_py.output = ../binding/protocols/${QMAKE_FILE_BASE}_pb2.py +protobuf_py.commands = $$escape_expand(\n) +#protobuf_py.commands = protoc --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_py.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_py From 13ff09f42164d31c6a8621791d7b687e0cbba7d2 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 4 Jun 2014 07:35:43 +0530 Subject: [PATCH 247/294] CLI: Added BLOB RPC support to python binding; updated the binding example to use the RPC; added a convenience method - saveCaptureBuffer() to DroneProxy --- binding/core.py | 11 +++++++++++ binding/example.py | 31 +++++++++++++++++++++++++++++-- binding/rpc.py | 18 ++++++++++++++++-- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/binding/core.py b/binding/core.py index a25d965..994d236 100644 --- a/binding/core.py +++ b/binding/core.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see +import os from rpc import OstinatoRpcChannel, OstinatoRpcController import protocols.protocol_pb2 as ost_pb @@ -40,9 +41,19 @@ class DroneProxy(object): def connect(self): self.channel.connect(self.host, self.port) + def disconnect(self): + self.channel.disconnect() + def callRpcMethod(self, method_name, request): controller = OstinatoRpcController() ost_pb.OstService_Stub.__dict__[method_name]( self.stub, controller, request, None) return controller.response + def saveCaptureBuffer(self, buffer, file_name): + f= open(file_name, 'wb') + f.write(buffer) + f.flush() + os.fsync(f.fileno()) + f.close() + diff --git a/binding/example.py b/binding/example.py index 54d13ef..2150222 100644 --- a/binding/example.py +++ b/binding/example.py @@ -1,7 +1,10 @@ +#! /usr/bin/env python + # standard modules +import logging +import os import sys import time -import logging # ostinato modules - prepend 'ostinato.' to the module names when using # an installed package i.e ostinato.core and ostinato.protocols.xxx @@ -88,7 +91,10 @@ try: drone.clearStats(tx_port) drone.clearStats(rx_port) - # start transmit + # start capture and transmit + log.info('starting capture') + drone.startCapture(rx_port) + time.sleep(1) log.info('starting transmit') drone.startTx(tx_port) @@ -96,6 +102,12 @@ try: log.info('waiting for transmit to finish ...') time.sleep(7) + # stop transmit and capture + log.info('stopping transmit') + drone.stopTx(tx_port) + log.info('stopping capture') + drone.stopCapture(rx_port) + # get tx/rx stats log.info('retreiving stats') tx_stats = drone.getStats(tx_port) @@ -106,6 +118,21 @@ try: log.info('tx pkts = %d, rx pkts = %d' % (tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts)) + # retrieve and dump received packets + log.info('getting Rx capture buffer') + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + os.system('tshark -r capture.pcap') + os.remove('capture.pcap') + + # delete streams + log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) + drone.deleteStream(stream_id) + + # bye for now + drone.disconnect() + except Exception, ex: log.exception(ex) sys.exit(1) diff --git a/binding/rpc.py b/binding/rpc.py index 6da362f..1d68ad2 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -31,13 +31,20 @@ class OstinatoRpcChannel(RpcChannel): def connect(self, host, port): self.sock.connect((host, port)) + def disconnect(self): + self.sock.close() + def CallMethod(self, method, controller, request, response_class, done): OST_PB_MSG_HDR_SIZE = 8 OST_PB_MSG_TYPE_REQUEST = 1 + OST_PB_MSG_TYPE_RESPONSE = 2 + OST_PB_MSG_TYPE_BLOB = 3 + req = request.SerializeToString() self.sock.sendall(struct.pack('>HHI', OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) + # receive and parse header hdr = '' while len(hdr) < OST_PB_MSG_HDR_SIZE: chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) @@ -47,6 +54,7 @@ class OstinatoRpcChannel(RpcChannel): (type, method, resp_len) = struct.unpack('>HHI', hdr) + # receive and parse the actual response message resp = '' while len(resp) < resp_len: chunk = self.sock.recv(resp_len - len(resp)) @@ -54,8 +62,14 @@ class OstinatoRpcChannel(RpcChannel): raise RuntimeError("socket connection broken") resp = resp + chunk - response = response_class() - response.ParseFromString(resp) + if type == OST_PB_MSG_TYPE_RESPONSE: + response = response_class() + response.ParseFromString(resp) + elif type == OST_PB_MSG_TYPE_BLOB: + response = resp + else: + print 'unsupported msg type %d received in respone to %s' % \ + type, method.name controller.response = response From d3b9d9be837ae04a11a4b4a2632fe255a1ad4c5b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 7 Jun 2014 20:12:51 +0530 Subject: [PATCH 248/294] Updated python packaging --- binding/README.txt | 7 +++++++ binding/__init__.py | 9 +++++++++ binding/setup.py | 40 ++++++++++++++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/binding/README.txt b/binding/README.txt index e69de29..e709125 100644 --- a/binding/README.txt +++ b/binding/README.txt @@ -0,0 +1,7 @@ +======== +Ostinato +======== + +Ostinato provides a scripting interface to the Ostinato Packet/Traffic Generator and Analyzer + +Documentation is available in the wiki at http://ostinato.org diff --git a/binding/__init__.py b/binding/__init__.py index 161ae68..8d68b2f 100644 --- a/binding/__init__.py +++ b/binding/__init__.py @@ -15,3 +15,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see +import json +from os.path import dirname + +with open(dirname(__file__) + '/pkg_info.json') as f: + _info = json.load(f) + +__version__ = _info['version'] +__revision__ = _info['revision'] + diff --git a/binding/setup.py b/binding/setup.py index a36face..e09bcc4 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -15,28 +15,56 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see +import json import os +import re import shutil import sys from setuptools import setup def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() + return open(fname).read() -if sys.argv[1] == 'clean_sdist': +def get_pkg_info(): + info = {} + t = open('../server/version.cpp').read() + info['version'] = re.search('version = "([^"]*)"', t).group(1) + info['revision'] = re.search('revision = "([^"]*)"', t).group(1) + return info + +# ------- script starts from here ------- # + +if len(sys.argv) >= 2 and sys.argv[1] == 'clean_sdist': shutil.rmtree('dist', ignore_errors = True) shutil.rmtree('ostinato.egg-info', ignore_errors = True) + if os.path.exists('pkg_info.json'): + os.remove('pkg_info.json') sys.exit(0) +if len(sys.argv) >= 2 and sys.argv[1] == 'sdist': + if os.path.split(os.getcwd())[1] != 'binding': + print 'This script needs to be run from the binding directory' + print 'Current Working Directory is %s' % os.getcwd() + sys.exit(1) + + pkg_info = get_pkg_info() + with open('pkg_info.json', 'wt') as f: + json.dump(pkg_info, f, indent=4) +else: + with open('pkg_info.json') as f: + pkg_info = json.load(f) + setup(name = 'ostinato', - version = 'FIXME', + version = pkg_info['version'], author = 'Srivats P', author_email = 'pstavirs@gmail.com', license = "GPLv3+", url = 'http://ostinato.org', description = 'Ostinato is a network packet and traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. It features custom packet crafting via a GUI or a script', long_description = read('README.txt'), - install_requires = ['google.protobuf>=2.3'], - packages=['ostinato', 'ostinato.protocols'], - package_dir={'ostinato': ''} + install_requires = ['protobuf>=2.3.0'], + packages = ['ostinato', 'ostinato.protocols'], + package_dir = {'ostinato': '.'}, + package_data = {'ostinato': ['pkg_info.json']}, ) + From 8f51e6f07a774c57c718f4bb4a9d1482c8d58da6 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 7 Jun 2014 20:58:50 +0530 Subject: [PATCH 249/294] Added classifiers to the python packaging --- binding/setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/binding/setup.py b/binding/setup.py index e09bcc4..3e9040a 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -66,5 +66,12 @@ setup(name = 'ostinato', packages = ['ostinato', 'ostinato.protocols'], package_dir = {'ostinato': '.'}, package_data = {'ostinato': ['pkg_info.json']}, + platforms = ['Any'], + classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Telecommunications Industry', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Topic :: Software Development :: Testing :: Traffic Generation', + 'Topic :: System :: Networking'] ) From 3b4b5a19b92a44cb8041aedd934b3b810d6855a8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 8 Jun 2014 19:10:49 +0530 Subject: [PATCH 250/294] Moved generation of pkg_info from setup.py to qmake/make - version.pri refactored suitably. Reimplemented sdist_clean as a new setuptools command --- .hgignore | 2 ++ binding/binding.pro | 4 +++ binding/setup.py | 60 ++++++++++++++++++++++++--------------------- client/ostinato.pro | 2 +- ost.pro | 4 ++- server/drone.pro | 2 +- version.pri | 51 +++++++++++++++++++++++++++----------- 7 files changed, 80 insertions(+), 45 deletions(-) create mode 100644 binding/binding.pro diff --git a/.hgignore b/.hgignore index 7e37eb9..0bc7b92 100644 --- a/.hgignore +++ b/.hgignore @@ -4,6 +4,8 @@ syntax: glob *.pyc *.o *.a +*.dll +*.so *.exe *.app drone diff --git a/binding/binding.pro b/binding/binding.pro new file mode 100644 index 0000000..68b7a58 --- /dev/null +++ b/binding/binding.pro @@ -0,0 +1,4 @@ +TEMPLATE = lib +CONFIG += pkg_info + +include(../version.pri) diff --git a/binding/setup.py b/binding/setup.py index 3e9040a..96f0810 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -17,42 +17,43 @@ import json import os -import re import shutil import sys -from setuptools import setup +from setuptools import Command, setup +from setuptools.command.sdist import sdist as _sdist def read(fname): - return open(fname).read() + return open(os.path.join(os.path.dirname(__file__), fname)).read() -def get_pkg_info(): - info = {} - t = open('../server/version.cpp').read() - info['version'] = re.search('version = "([^"]*)"', t).group(1) - info['revision'] = re.search('revision = "([^"]*)"', t).group(1) - return info - -# ------- script starts from here ------- # - -if len(sys.argv) >= 2 and sys.argv[1] == 'clean_sdist': - shutil.rmtree('dist', ignore_errors = True) - shutil.rmtree('ostinato.egg-info', ignore_errors = True) - if os.path.exists('pkg_info.json'): - os.remove('pkg_info.json') - sys.exit(0) - -if len(sys.argv) >= 2 and sys.argv[1] == 'sdist': +def ensure_cwd(): if os.path.split(os.getcwd())[1] != 'binding': - print 'This script needs to be run from the binding directory' + print 'ERROR: This script needs to be run from the binding directory' print 'Current Working Directory is %s' % os.getcwd() sys.exit(1) - pkg_info = get_pkg_info() - with open('pkg_info.json', 'wt') as f: - json.dump(pkg_info, f, indent=4) -else: - with open('pkg_info.json') as f: - pkg_info = json.load(f) +class sdist(_sdist): + def run(self): + ensure_cwd() + _sdist.run(self) + +class sdist_clean(Command): + description = 'clean stuff generated by sdist' + user_options = [] + def initialize_options(self): + return + + def finalize_options(self): + return + + def run(self): + ensure_cwd() + shutil.rmtree('dist', ignore_errors = True) + shutil.rmtree('ostinato.egg-info', ignore_errors = True) + +# ------- script starts from here ------- # + +with open(os.path.join(os.path.dirname(__file__), 'pkg_info.json')) as f: + pkg_info = json.load(f) setup(name = 'ostinato', version = pkg_info['version'], @@ -72,6 +73,9 @@ setup(name = 'ostinato', 'Intended Audience :: Telecommunications Industry', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 'Topic :: Software Development :: Testing :: Traffic Generation', - 'Topic :: System :: Networking'] + 'Topic :: System :: Networking'], + cmdclass={ + 'sdist': sdist, + 'sdist_clean': sdist_clean}, ) diff --git a/client/ostinato.pro b/client/ostinato.pro index d5fd459..9a4af25 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -1,5 +1,5 @@ TEMPLATE = app -CONFIG += qt +CONFIG += qt ver_info macx: TARGET = Ostinato win32:RC_FILE = ostinato.rc macx:ICON = icons/logo.icns diff --git a/ost.pro b/ost.pro index 9e8bbea..0e758eb 100644 --- a/ost.pro +++ b/ost.pro @@ -6,4 +6,6 @@ SUBDIRS = \ common/ostproto.pro \ common/ostprotogui.pro \ server/drone.pro \ - client/ostinato.pro + client/ostinato.pro \ + binding/binding.pro + diff --git a/server/drone.pro b/server/drone.pro index be69521..54efd8b 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -1,5 +1,5 @@ TEMPLATE = app -CONFIG += qt +CONFIG += qt ver_info QT += network script QT -= gui DEFINES += HAVE_REMOTE WPCAP diff --git a/version.pri b/version.pri index 718ce69..a76c889 100644 --- a/version.pri +++ b/version.pri @@ -2,18 +2,41 @@ APP_VERSION = 0.5.1 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ -APP_VERSION_FILE = version.cpp -revtarget.target = $$APP_VERSION_FILE -win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ - "const char *revision = \"$$APP_REVISION\";" \ - > $$APP_VERSION_FILE -unix:revtarget.commands = echo \ - "\"const char *version = \\\"$$APP_VERSION\\\";" \ - "const char *revision = \\\"$$APP_REVISION\\\";\"" \ - > $$APP_VERSION_FILE -revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS -SOURCES += $$APP_VERSION_FILE -QMAKE_EXTRA_TARGETS += revtarget -POST_TARGETDEPS += $$APP_VERSION_FILE -QMAKE_DISTCLEAN += $$APP_VERSION_FILE +ver_info { + APP_VERSION_FILE = version.cpp + revtarget.target = $$APP_VERSION_FILE + win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE + unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE + revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + + SOURCES += $$APP_VERSION_FILE + QMAKE_EXTRA_TARGETS += revtarget + POST_TARGETDEPS += $$APP_VERSION_FILE + QMAKE_DISTCLEAN += $$APP_VERSION_FILE +} + +pkg_info { + PKG_INFO_FILE = pkg_info.json + pkginfo.target = $$PKG_INFO_FILE + pkginfo.CONFIG = recursive + win32:pkginfo.commands = echo "{" \ + " \"version\": \"$$APP_VERSION\"," \ + " \"revision\": \"$$APP_REVISION\"" \ + "}" \ + > $$PKG_INFO_FILE + unix:pkginfo.commands = echo "\"{" \ + " \\\"version\\\": \\\"$$APP_VERSION\\\"," \ + " \\\"revision\\\": \\\"$$APP_REVISION\\\"" \ + "}\"" \ + > $$PKG_INFO_FILE + + QMAKE_EXTRA_TARGETS += pkginfo + POST_TARGETDEPS += $$PKG_INFO_FILE + QMAKE_DISTCLEAN += $$PKG_INFO_FILE +} From f75a99c834292e756036fdc913e811da4702b33c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 10 Jun 2014 18:24:39 +0530 Subject: [PATCH 251/294] CLI: modified python binding example script to take user input; enhanced DroneProxy to assume a default request type of Void if none is provided by user to make it intuitive for end users --- binding/core.py | 3 ++- binding/example.py | 55 ++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/binding/core.py b/binding/core.py index 994d236..ac28707 100644 --- a/binding/core.py +++ b/binding/core.py @@ -26,9 +26,10 @@ class DroneProxy(object): self.port = port_number self.channel = OstinatoRpcChannel() self.stub = ost_pb.OstService_Stub(self.channel) + self.void = ost_pb.Void() for method in self.stub.GetDescriptor().methods: - fn = lambda request, method_name=method.name: \ + fn = lambda request=self.void, method_name=method.name: \ self.callRpcMethod(method_name, request) self.__dict__[method.name] = fn diff --git a/binding/example.py b/binding/example.py index 2150222..6b64219 100644 --- a/binding/example.py +++ b/binding/example.py @@ -12,14 +12,18 @@ from core import ost_pb, DroneProxy from protocols.mac_pb2 import mac from protocols.ip4_pb2 import ip4, Ip4 +# initialize defaults host_name = '127.0.0.1' -tx_port_number = 1 -rx_port_number = 1 +tx_port_number = 0 +rx_port_number = 0 # setup logging log = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) +s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) +host_name = s or host_name + drone = DroneProxy(host_name) try: @@ -28,29 +32,38 @@ try: % (drone.hostName(), drone.portNumber())) drone.connect() + # retreive port id list + log.info('retreiving port list') + port_id_list = drone.getPortIdList() + + # retreive port config list + log.info('retreiving port config for all ports') + port_config_list = drone.getPortConfig(port_id_list) + + # print port list and get tx/rx port id + print 'Port List' + print '---------' + for port in port_config_list.port: + print '%d.%s (%s)' % (port.port_id.id, port.name, port.description) + # use a loopback port as default tx/rx port + if ('lo' in port.name or 'loopback' in port.description.lower()): + tx_port_number = port.port_id.id + rx_port_number = port.port_id.id + + p = raw_input('Tx Port Id [%d]: ' % (tx_port_number)) + if p: + tx_port_number = int(p) + + p = raw_input('Rx Port Id [%d]: ' % (rx_port_number)) + if p: + rx_port_number = int(p) + tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; rx_port = ost_pb.PortIdList() rx_port.port_id.add().id = rx_port_number; - # verify tx and rx ports exist - log.info('verifying tx_port %d' % tx_port.port_id[0].id) - port_config_list = drone.getPortConfig(tx_port) - log.info('-->' + port_config_list.__str__()) - if len(port_config_list.port) <= 0: - log.error('invalid tx_port' - + tx_port_number) - sys.exit(1) - - log.info('verifying rx_port %d' % rx_port.port_id[0].id) - port_config_list = drone.getPortConfig(rx_port) - log.info('-->' + port_config_list.__str__()) - if len(port_config_list.port) <= 0: - log.error('invalid rx_port' - + rx_port_number) - sys.exit(1) - # add a stream stream_id = ost_pb.StreamIdList() stream_id.port_id.CopyFrom(tx_port.port_id[0]) @@ -113,8 +126,8 @@ try: tx_stats = drone.getStats(tx_port) rx_stats = drone.getStats(rx_port) - log.info('--> (tx_stats)' + tx_stats.__str__()) - log.info('--> (rx_stats)' + rx_stats.__str__()) + #log.info('--> (tx_stats)' + tx_stats.__str__()) + #log.info('--> (rx_stats)' + rx_stats.__str__()) log.info('tx pkts = %d, rx pkts = %d' % (tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts)) From 02ac4e7dffd1f408d50cc46e63cb5efbd32f617d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 10 Jun 2014 19:20:45 +0530 Subject: [PATCH 252/294] CLI: Added LICENSE.txt to python packaging; explicitly identified example.py as a script for packaging --- binding/LICENSE.txt | 674 ++++++++++++++++++++++++++++++++++++++++++++ binding/setup.py | 4 +- 2 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 binding/LICENSE.txt diff --git a/binding/LICENSE.txt b/binding/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/binding/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/binding/setup.py b/binding/setup.py index 96f0810..1bcc711 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # Copyright (C) 2014 Srivats P. # # This file is part of "Ostinato" @@ -66,7 +67,8 @@ setup(name = 'ostinato', install_requires = ['protobuf>=2.3.0'], packages = ['ostinato', 'ostinato.protocols'], package_dir = {'ostinato': '.'}, - package_data = {'ostinato': ['pkg_info.json']}, + package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']}, + scripts = ['example.py'], platforms = ['Any'], classifiers = [ 'Development Status :: 5 - Production/Stable', From 30e949627085217553c3201bbcbe1dcd7ff5b83f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 11 Jun 2014 20:39:55 +0530 Subject: [PATCH 253/294] CLI: Added exception handling to python RPC --- .hgignore | 1 + binding/rpc.py | 99 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 23 deletions(-) diff --git a/.hgignore b/.hgignore index 0bc7b92..48fc886 100644 --- a/.hgignore +++ b/.hgignore @@ -27,6 +27,7 @@ Makefile* # ostinato generated files version.cpp +pkg_info.json # vim swap files *.swp diff --git a/binding/rpc.py b/binding/rpc.py index 1d68ad2..9e3be2d 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -15,10 +15,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see +from google.protobuf.message import EncodeError, DecodeError from google.protobuf.service import RpcChannel from google.protobuf.service import RpcController import socket import struct +import sys class OstinatoRpcController(RpcController): def __init__(self): @@ -29,7 +31,13 @@ class OstinatoRpcChannel(RpcChannel): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def connect(self, host, port): - self.sock.connect((host, port)) + self.peer = '%s:%d' % (host, port) + try: + self.sock.connect((host, port)) + except socket.error, e: + print 'ERROR: Unable to connect to Drone %s (%s)' % ( + self.peer, str(e)) + sys.exit(1) def disconnect(self): self.sock.close() @@ -40,36 +48,81 @@ class OstinatoRpcChannel(RpcChannel): OST_PB_MSG_TYPE_RESPONSE = 2 OST_PB_MSG_TYPE_BLOB = 3 - req = request.SerializeToString() - self.sock.sendall(struct.pack('>HHI', - OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) + try: + req = request.SerializeToString() + self.sock.sendall(struct.pack('>HHI', + OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) + except EncodeError, e: + print 'ERROR: Failed to serialize %s arg for RPC %s() ' \ + 'to Drone %s (%s)' % ( + type(request).__name__, method.name, self.peer, e) + sys.exit(1) + except socket.error, e: + print 'ERROR: Failed to invoke RPC %s() to Drone %s (%s)' % ( + method.name, self.peer, e) + sys.exit(1) # receive and parse header - hdr = '' - while len(hdr) < OST_PB_MSG_HDR_SIZE: - chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) - if chunk == '': - raise RuntimeError("socket connection broken") - hdr = hdr + chunk + try: + hdr = '' + while len(hdr) < OST_PB_MSG_HDR_SIZE: + chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) + if chunk == '': + raise RuntimeError("socket connection closed by peer") + hdr = hdr + chunk + except socket.error, e: + print 'ERROR: Failed to receive msg reply for RPC %s() ' \ + 'from Drone %s (%s)' % ( + method.name, self.peer, e) + sys.exit(1) + except RuntimeError, e: + print 'ERROR: Drone %s closed connection receiving msg reply ' \ + 'for RPC %s() (%s)' % ( + self.peer, method.name, e) + sys.exit(1) - (type, method, resp_len) = struct.unpack('>HHI', hdr) + (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) + + # verify response method is same as the one requested + if method_index != method.index: + print 'ERROR: Received Reply for Method %d; expecting reply for ' \ + 'method %d (%s)' % ( + method_index, method.index, method.name) + sys.exit(1) # receive and parse the actual response message - resp = '' - while len(resp) < resp_len: - chunk = self.sock.recv(resp_len - len(resp)) - if chunk == '': - raise RuntimeError("socket connection broken") - resp = resp + chunk + try: + resp = '' + while len(resp) < resp_len: + chunk = self.sock.recv(resp_len - len(resp)) + if chunk == '': + raise RuntimeError("socket connection closed by peer") + resp = resp + chunk + except socket.error, e: + print 'ERROR: Failed to receive reply for RPC %s() ' \ + 'from Drone %s (%s)' % ( + method.name, self.peer, e) + sys.exit(1) + except RuntimeError, e: + print 'ERROR: Drone %s closed connection receiving reply ' \ + 'for RPC %s() (%s)' % ( + self.peer, method.name, e) + sys.exit(1) - if type == OST_PB_MSG_TYPE_RESPONSE: - response = response_class() - response.ParseFromString(resp) - elif type == OST_PB_MSG_TYPE_BLOB: + if msg_type == OST_PB_MSG_TYPE_RESPONSE: + try: + response = response_class() + response.ParseFromString(resp) + except DecodeError, e: + print 'ERROR: Failed to parse %s response for RPC %s() ' \ + 'from Drone %s (%s)' % ( + type(response).__name__, method.name, self.peer, e) + sys.exit(1) + elif msg_type == OST_PB_MSG_TYPE_BLOB: response = resp else: - print 'unsupported msg type %d received in respone to %s' % \ - type, method.name + print 'ERROR: unsupported msg type %d received in respone to %s' % \ + msg_type, method.name controller.response = response From 2a83b1a61b15372d07851de282d4059d7ed37386 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 12 Jun 2014 07:11:32 +0530 Subject: [PATCH 254/294] CLI: cleaned up exceptions to make code more readable --- binding/rpc.py | 130 +++++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/binding/rpc.py b/binding/rpc.py index 9e3be2d..791ef1d 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -22,6 +22,27 @@ import socket import struct import sys +class PeerClosedConnError(Exception): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class RpcError(Exception): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class RpcMismatchError(Exception): + def __init__(self, msg, received, expected): + self.msg = msg + self.received = received + self.expected = expected + def __str__(self): + return '%s - Expected method %d, Received method %d' % ( + self.msg, self.expected, self.received) + class OstinatoRpcController(RpcController): def __init__(self): super(OstinatoRpcController, self).__init__() @@ -35,9 +56,10 @@ class OstinatoRpcChannel(RpcChannel): try: self.sock.connect((host, port)) except socket.error, e: - print 'ERROR: Unable to connect to Drone %s (%s)' % ( + error = 'ERROR: Unable to connect to Drone %s (%s)' % ( self.peer, str(e)) - sys.exit(1) + print error + raise def disconnect(self): self.sock.close() @@ -48,83 +70,75 @@ class OstinatoRpcChannel(RpcChannel): OST_PB_MSG_TYPE_RESPONSE = 2 OST_PB_MSG_TYPE_BLOB = 3 + error = '' try: req = request.SerializeToString() self.sock.sendall(struct.pack('>HHI', OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) - except EncodeError, e: - print 'ERROR: Failed to serialize %s arg for RPC %s() ' \ - 'to Drone %s (%s)' % ( - type(request).__name__, method.name, self.peer, e) - sys.exit(1) - except socket.error, e: - print 'ERROR: Failed to invoke RPC %s() to Drone %s (%s)' % ( - method.name, self.peer, e) - sys.exit(1) - # receive and parse header - try: + # receive and parse header hdr = '' while len(hdr) < OST_PB_MSG_HDR_SIZE: chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) if chunk == '': - raise RuntimeError("socket connection closed by peer") + raise PeerClosedConnError('connection closed by peer') hdr = hdr + chunk - except socket.error, e: - print 'ERROR: Failed to receive msg reply for RPC %s() ' \ - 'from Drone %s (%s)' % ( - method.name, self.peer, e) - sys.exit(1) - except RuntimeError, e: - print 'ERROR: Drone %s closed connection receiving msg reply ' \ - 'for RPC %s() (%s)' % ( - self.peer, method.name, e) - sys.exit(1) - (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) + (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) - # verify response method is same as the one requested - if method_index != method.index: - print 'ERROR: Received Reply for Method %d; expecting reply for ' \ - 'method %d (%s)' % ( - method_index, method.index, method.name) - sys.exit(1) - - # receive and parse the actual response message - try: + # receive and parse the actual response message resp = '' while len(resp) < resp_len: chunk = self.sock.recv(resp_len - len(resp)) if chunk == '': - raise RuntimeError("socket connection closed by peer") + raise PeerClosedConnError('connection closed by peer') resp = resp + chunk + + # verify response method is same as the one requested + if method_index != method.index: + raise RpcMismatchError('RPC mismatch', + expected = method.index, received = method_index) + + if msg_type == OST_PB_MSG_TYPE_RESPONSE: + response = response_class() + response.ParseFromString(resp) + elif msg_type == OST_PB_MSG_TYPE_BLOB: + response = resp + else: + raise RpcError('unknown RPC msg type %d' % msg_type) + + controller.response = response + except socket.error, e: - print 'ERROR: Failed to receive reply for RPC %s() ' \ - 'from Drone %s (%s)' % ( + error = 'ERROR: RPC %s() to Drone %s failed (%s)' % ( method.name, self.peer, e) - sys.exit(1) - except RuntimeError, e: - print 'ERROR: Drone %s closed connection receiving reply ' \ + raise + except PeerClosedConnError, e: + error = 'ERROR: Drone %s closed connection receiving reply ' \ 'for RPC %s() (%s)' % ( self.peer, method.name, e) - sys.exit(1) - - if msg_type == OST_PB_MSG_TYPE_RESPONSE: - try: - response = response_class() - response.ParseFromString(resp) - except DecodeError, e: - print 'ERROR: Failed to parse %s response for RPC %s() ' \ - 'from Drone %s (%s)' % ( - type(response).__name__, method.name, self.peer, e) - sys.exit(1) - elif msg_type == OST_PB_MSG_TYPE_BLOB: - response = resp - else: - print 'ERROR: unsupported msg type %d received in respone to %s' % \ - msg_type, method.name - - controller.response = response + raise + except EncodeError, e: + error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \ + 'to Drone %s (%s)' % ( + type(request).__name__, method.name, self.peer, e) + raise + except DecodeError, e: + error = 'ERROR: Failed to parse %s response for RPC %s() ' \ + 'from Drone %s (%s)' % ( + type(response).__name__, method.name, self.peer, e) + raise + except RpcMismatchError, e: + error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % ( + method.name, e) + raise + except RpcError, e: + error = 'ERROR: Unknown reply received for RPC %s() (%s) ' % ( + method.name, e) + raise + finally: + if error: + print error From 3406d686973a065c835ffb8dc9416b284f3730f4 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 13 Jun 2014 20:55:15 +0530 Subject: [PATCH 255/294] CLI: Added logging to the ostinato modules for debugging; -d option to example.py skips user input and uses default values instead --- binding/__init__.py | 2 ++ binding/example.py | 37 ++++++++++++++++++++++++++----------- binding/rpc.py | 40 ++++++++++++++++++++++++++++++---------- binding/setup.py | 1 - 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/binding/__init__.py b/binding/__init__.py index 8d68b2f..76ca301 100644 --- a/binding/__init__.py +++ b/binding/__init__.py @@ -16,6 +16,7 @@ # along with this program. If not, see import json +import logging from os.path import dirname with open(dirname(__file__) + '/pkg_info.json') as f: @@ -24,3 +25,4 @@ with open(dirname(__file__) + '/pkg_info.json') as f: __version__ = _info['version'] __revision__ = _info['revision'] +__log__ = logging.getLogger('ostinato') diff --git a/binding/example.py b/binding/example.py index 6b64219..b2f83b9 100644 --- a/binding/example.py +++ b/binding/example.py @@ -6,23 +6,37 @@ import os import sys import time -# ostinato modules - prepend 'ostinato.' to the module names when using -# an installed package i.e ostinato.core and ostinato.protocols.xxx +# ostinato modules +# (user scripts using the installed package should prepend ostinato. i.e +# ostinato.core and ostinato.protocols) from core import ost_pb, DroneProxy from protocols.mac_pb2 import mac from protocols.ip4_pb2 import ip4, Ip4 # initialize defaults +use_defaults = False host_name = '127.0.0.1' tx_port_number = 0 rx_port_number = 0 # setup logging log = logging.getLogger(__name__) -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) -s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) -host_name = s or host_name +# command-line option/arg processing +if len(sys.argv) > 1: + if sys.argv[1] in ('-d', '--use-defaults'): + use_defaults = True + if sys.argv[1] in ('-h', '--help'): + print '%s [OPTION]...' % (sys.argv[0]) + print 'Options:' + print ' -d --use-defaults run using default values' + print ' -h --help show this help' + sys.exit(0) + +if not use_defaults: + s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) + host_name = s or host_name drone = DroneProxy(host_name) @@ -50,13 +64,14 @@ try: tx_port_number = port.port_id.id rx_port_number = port.port_id.id - p = raw_input('Tx Port Id [%d]: ' % (tx_port_number)) - if p: - tx_port_number = int(p) + if not use_defaults: + p = raw_input('Tx Port Id [%d]: ' % (tx_port_number)) + if p: + tx_port_number = int(p) - p = raw_input('Rx Port Id [%d]: ' % (rx_port_number)) - if p: - rx_port_number = int(p) + p = raw_input('Rx Port Id [%d]: ' % (rx_port_number)) + if p: + rx_port_number = int(p) tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; diff --git a/binding/rpc.py b/binding/rpc.py index 791ef1d..7bc121a 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -18,6 +18,7 @@ from google.protobuf.message import EncodeError, DecodeError from google.protobuf.service import RpcChannel from google.protobuf.service import RpcController +import logging import socket import struct import sys @@ -49,10 +50,13 @@ class OstinatoRpcController(RpcController): class OstinatoRpcChannel(RpcChannel): def __init__(self): + self.log = logging.getLogger('ostinato.rpc') + self.log.debug('opening socket') self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def connect(self, host, port): self.peer = '%s:%d' % (host, port) + self.log.debug('connecting to %s', self.peer) try: self.sock.connect((host, port)) except socket.error, e: @@ -62,31 +66,40 @@ class OstinatoRpcChannel(RpcChannel): raise def disconnect(self): + self.log.debug('closing socket') self.sock.close() def CallMethod(self, method, controller, request, response_class, done): - OST_PB_MSG_HDR_SIZE = 8 - OST_PB_MSG_TYPE_REQUEST = 1 - OST_PB_MSG_TYPE_RESPONSE = 2 - OST_PB_MSG_TYPE_BLOB = 3 + MSG_HDR_SIZE = 8 + MSG_TYPE_REQUEST = 1 + MSG_TYPE_RESPONSE = 2 + MSG_TYPE_BLOB = 3 error = '' try: + self.log.debug('invoking RPC %s(%s): %s', method.name, + type(request).__name__, response_class.__name__) + self.log.debug('serializing request arg %s', request) req = request.SerializeToString() - self.sock.sendall(struct.pack('>HHI', - OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req) + hdr = struct.pack('>HHI', MSG_TYPE_REQUEST, method.index, len(req)) + self.log.debug('req.hdr = %r', hdr) + self.sock.sendall(hdr + req) # receive and parse header + self.log.debug('receiving response hdr') hdr = '' - while len(hdr) < OST_PB_MSG_HDR_SIZE: - chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr)) + while len(hdr) < MSG_HDR_SIZE: + chunk = self.sock.recv(MSG_HDR_SIZE - len(hdr)) if chunk == '': raise PeerClosedConnError('connection closed by peer') hdr = hdr + chunk (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) + self.log.debug('resp hdr: type = %d, method = %d, len = %d', + msg_type, method_index, resp_len) # receive and parse the actual response message + self.log.debug('receiving response data') resp = '' while len(resp) < resp_len: chunk = self.sock.recv(resp_len - len(resp)) @@ -99,10 +112,11 @@ class OstinatoRpcChannel(RpcChannel): raise RpcMismatchError('RPC mismatch', expected = method.index, received = method_index) - if msg_type == OST_PB_MSG_TYPE_RESPONSE: + if msg_type == MSG_TYPE_RESPONSE: response = response_class() response.ParseFromString(resp) - elif msg_type == OST_PB_MSG_TYPE_BLOB: + self.log.debug('parsed response %s', response) + elif msg_type == MSG_TYPE_BLOB: response = resp else: raise RpcError('unknown RPC msg type %d' % msg_type) @@ -112,29 +126,35 @@ class OstinatoRpcChannel(RpcChannel): except socket.error, e: error = 'ERROR: RPC %s() to Drone %s failed (%s)' % ( method.name, self.peer, e) + self.log.exception(error+e) raise except PeerClosedConnError, e: error = 'ERROR: Drone %s closed connection receiving reply ' \ 'for RPC %s() (%s)' % ( self.peer, method.name, e) + self.log.exception(error) raise except EncodeError, e: error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \ 'to Drone %s (%s)' % ( type(request).__name__, method.name, self.peer, e) + self.log.exception(error) raise except DecodeError, e: error = 'ERROR: Failed to parse %s response for RPC %s() ' \ 'from Drone %s (%s)' % ( type(response).__name__, method.name, self.peer, e) + self.log.exception(error) raise except RpcMismatchError, e: error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % ( method.name, e) + self.log.exception(error) raise except RpcError, e: error = 'ERROR: Unknown reply received for RPC %s() (%s) ' % ( method.name, e) + self.log.exception(error) raise finally: if error: diff --git a/binding/setup.py b/binding/setup.py index 1bcc711..97dac8b 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -68,7 +68,6 @@ setup(name = 'ostinato', packages = ['ostinato', 'ostinato.protocols'], package_dir = {'ostinato': '.'}, package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']}, - scripts = ['example.py'], platforms = ['Any'], classifiers = [ 'Development Status :: 5 - Production/Stable', From 288380227e66a40bd391e478f1925dfb7f11c0fd Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2014 15:31:02 +0530 Subject: [PATCH 256/294] Minor changes to ease porting to Python 3 in the future --- binding/example.py | 16 ++++++++-------- binding/rpc.py | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/binding/example.py b/binding/example.py index b2f83b9..b1f5f14 100644 --- a/binding/example.py +++ b/binding/example.py @@ -28,10 +28,10 @@ if len(sys.argv) > 1: if sys.argv[1] in ('-d', '--use-defaults'): use_defaults = True if sys.argv[1] in ('-h', '--help'): - print '%s [OPTION]...' % (sys.argv[0]) - print 'Options:' - print ' -d --use-defaults run using default values' - print ' -h --help show this help' + print('%s [OPTION]...' % (sys.argv[0])) + print('Options:') + print(' -d --use-defaults run using default values') + print(' -h --help show this help') sys.exit(0) if not use_defaults: @@ -55,10 +55,10 @@ try: port_config_list = drone.getPortConfig(port_id_list) # print port list and get tx/rx port id - print 'Port List' - print '---------' + print('Port List') + print('---------') for port in port_config_list.port: - print '%d.%s (%s)' % (port.port_id.id, port.name, port.description) + print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) # use a loopback port as default tx/rx port if ('lo' in port.name or 'loopback' in port.description.lower()): tx_port_number = port.port_id.id @@ -161,6 +161,6 @@ try: # bye for now drone.disconnect() -except Exception, ex: +except Exception as ex: log.exception(ex) sys.exit(1) diff --git a/binding/rpc.py b/binding/rpc.py index 7bc121a..41ff5c8 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -59,10 +59,10 @@ class OstinatoRpcChannel(RpcChannel): self.log.debug('connecting to %s', self.peer) try: self.sock.connect((host, port)) - except socket.error, e: + except socket.error as e: error = 'ERROR: Unable to connect to Drone %s (%s)' % ( self.peer, str(e)) - print error + print(error) raise def disconnect(self): @@ -123,42 +123,42 @@ class OstinatoRpcChannel(RpcChannel): controller.response = response - except socket.error, e: + except socket.error as e: error = 'ERROR: RPC %s() to Drone %s failed (%s)' % ( method.name, self.peer, e) self.log.exception(error+e) raise - except PeerClosedConnError, e: + except PeerClosedConnError as e: error = 'ERROR: Drone %s closed connection receiving reply ' \ 'for RPC %s() (%s)' % ( self.peer, method.name, e) self.log.exception(error) raise - except EncodeError, e: + except EncodeError as e: error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \ 'to Drone %s (%s)' % ( type(request).__name__, method.name, self.peer, e) self.log.exception(error) raise - except DecodeError, e: + except DecodeError as e: error = 'ERROR: Failed to parse %s response for RPC %s() ' \ 'from Drone %s (%s)' % ( type(response).__name__, method.name, self.peer, e) self.log.exception(error) raise - except RpcMismatchError, e: + except RpcMismatchError as e: error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % ( method.name, e) self.log.exception(error) raise - except RpcError, e: + except RpcError as e: error = 'ERROR: Unknown reply received for RPC %s() (%s) ' % ( method.name, e) self.log.exception(error) raise finally: if error: - print error + print(error) From 278a85d606e20d150eae391b0094a6bf628484c0 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2014 17:02:40 +0530 Subject: [PATCH 257/294] CLI: renamed the python sdist as 'python-ostinato'; marked development status as Beta; classified it for use with python 2.7 only --- binding/README.txt | 8 ++++---- binding/setup.py | 12 +++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/binding/README.txt b/binding/README.txt index e709125..30540b0 100644 --- a/binding/README.txt +++ b/binding/README.txt @@ -1,7 +1,7 @@ -======== -Ostinato -======== +=============== +python-ostinato +=============== -Ostinato provides a scripting interface to the Ostinato Packet/Traffic Generator and Analyzer +python-ostinato provides python bindings for the Ostinato Packet/Traffic Generator and Analyzer Documentation is available in the wiki at http://ostinato.org diff --git a/binding/setup.py b/binding/setup.py index 97dac8b..1089132 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -28,8 +28,8 @@ def read(fname): def ensure_cwd(): if os.path.split(os.getcwd())[1] != 'binding': - print 'ERROR: This script needs to be run from the binding directory' - print 'Current Working Directory is %s' % os.getcwd() + print('ERROR: This script needs to be run from the binding directory') + print('Current Working Directory is %s' % os.getcwd()) sys.exit(1) class sdist(_sdist): @@ -49,14 +49,15 @@ class sdist_clean(Command): def run(self): ensure_cwd() shutil.rmtree('dist', ignore_errors = True) - shutil.rmtree('ostinato.egg-info', ignore_errors = True) + shutil.rmtree('python-ostinato.egg-info', ignore_errors = True) + shutil.rmtree('python_ostinato.egg-info', ignore_errors = True) # ------- script starts from here ------- # with open(os.path.join(os.path.dirname(__file__), 'pkg_info.json')) as f: pkg_info = json.load(f) -setup(name = 'ostinato', +setup(name = 'python-ostinato', version = pkg_info['version'], author = 'Srivats P', author_email = 'pstavirs@gmail.com', @@ -70,7 +71,8 @@ setup(name = 'ostinato', package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']}, platforms = ['Any'], classifiers = [ - 'Development Status :: 5 - Production/Stable', + 'Development Status :: 4 - Beta', + 'Programming Language :: Python :: 2.7', 'Intended Audience :: Telecommunications Industry', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 'Topic :: Software Development :: Testing :: Traffic Generation', From ace1a3694d43863a3328010fbaf89343aa48e8fb Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2014 17:16:41 +0530 Subject: [PATCH 258/294] CLI: changed python package description to match long_description --- binding/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/setup.py b/binding/setup.py index 1089132..9500271 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -63,7 +63,7 @@ setup(name = 'python-ostinato', author_email = 'pstavirs@gmail.com', license = "GPLv3+", url = 'http://ostinato.org', - description = 'Ostinato is a network packet and traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. It features custom packet crafting via a GUI or a script', + description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer', long_description = read('README.txt'), install_requires = ['protobuf>=2.3.0'], packages = ['ostinato', 'ostinato.protocols'], From d11e82cc3324de04e9388413724c21d478106f7c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2014 17:18:39 +0530 Subject: [PATCH 259/294] CLI: Changed python package long_description to match description --- binding/README.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/binding/README.txt b/binding/README.txt index 30540b0..b0de5ec 100644 --- a/binding/README.txt +++ b/binding/README.txt @@ -2,6 +2,8 @@ python-ostinato =============== -python-ostinato provides python bindings for the Ostinato Packet/Traffic Generator and Analyzer +python-ostinato provides python bindings for Ostinato. + +Ostinato is a network packet/traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. Documentation is available in the wiki at http://ostinato.org From a6bfb0d39312a8975d1518aaf577807a396fd5f8 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 14 Jun 2014 17:20:01 +0530 Subject: [PATCH 260/294] CLI: fixed extra space in description --- binding/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/setup.py b/binding/setup.py index 9500271..8c81b74 100644 --- a/binding/setup.py +++ b/binding/setup.py @@ -63,7 +63,7 @@ setup(name = 'python-ostinato', author_email = 'pstavirs@gmail.com', license = "GPLv3+", url = 'http://ostinato.org', - description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer', + description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer', long_description = read('README.txt'), install_requires = ['protobuf>=2.3.0'], packages = ['ostinato', 'ostinato.protocols'], From 0d3b36b943f6e3ce31926e11cb96a80682312b2a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 16 Jun 2014 19:53:03 +0530 Subject: [PATCH 261/294] CLI: minor changes to python binding code --- binding/__init__.py | 2 +- binding/example.py | 11 +++++++---- binding/rpc.py | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/binding/__init__.py b/binding/__init__.py index 76ca301..8c8376d 100644 --- a/binding/__init__.py +++ b/binding/__init__.py @@ -25,4 +25,4 @@ with open(dirname(__file__) + '/pkg_info.json') as f: __version__ = _info['version'] __revision__ = _info['revision'] -__log__ = logging.getLogger('ostinato') +__log__ = logging.getLogger(__name__) diff --git a/binding/example.py b/binding/example.py index b1f5f14..c84f6ae 100644 --- a/binding/example.py +++ b/binding/example.py @@ -91,9 +91,10 @@ try: stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) s = stream_cfg.stream.add() s.stream_id.id = stream_id.stream_id[0].id - s.core.is_enabled = 1 + s.core.is_enabled = True s.control.num_packets = 5 + # setup stream protocols as mac:eth2:ip4:udp:payload p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 @@ -104,9 +105,11 @@ try: p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber - p.Extensions[ip4].src_ip = 0x01020304 - p.Extensions[ip4].dst_ip = 0x05060708 - p.Extensions[ip4].dst_ip_mode = Ip4.e_im_inc_host + # reduce typing by creating a shorter reference to p.Extensions[ip4] + ip = p.Extensions[ip4] + ip.src_ip = 0x01020304 + ip.dst_ip = 0x05060708 + ip.dst_ip_mode = Ip4.e_im_inc_host s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber diff --git a/binding/rpc.py b/binding/rpc.py index 41ff5c8..aea0a53 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -50,7 +50,7 @@ class OstinatoRpcController(RpcController): class OstinatoRpcChannel(RpcChannel): def __init__(self): - self.log = logging.getLogger('ostinato.rpc') + self.log = logging.getLogger(__name__) self.log.debug('opening socket') self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -77,7 +77,7 @@ class OstinatoRpcChannel(RpcChannel): error = '' try: - self.log.debug('invoking RPC %s(%s): %s', method.name, + self.log.info('invoking RPC %s(%s): %s', method.name, type(request).__name__, response_class.__name__) self.log.debug('serializing request arg %s', request) req = request.SerializeToString() From 3770e4bcc30b0bc4b21f5f4bbe39c0e1924008e7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 17 Jun 2014 18:08:53 +0530 Subject: [PATCH 262/294] CLI: Added topology information to example.py --- binding/example.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/binding/example.py b/binding/example.py index c84f6ae..9ee39ad 100644 --- a/binding/example.py +++ b/binding/example.py @@ -34,6 +34,22 @@ if len(sys.argv) > 1: print(' -h --help show this help') sys.exit(0) +print('') +print('This example expects the following topology -') +print('') +print(' +-------+ +-------+') +print(' | |Tx--->----| |') +print(' | Drone | | DUT |') +print(' | |Rx---<----| |') +print(' +-------+ +-------+') +print('') +print('Drone has 2 ports connected to DUT. Packets sent on the Tx port') +print('are expected to be received back on the Rx port') +print('') +print('An easy way to simulate the above topology is to select the loopback') +print('port as both Tx and Rx ports') +print('') + if not use_defaults: s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) host_name = s or host_name From ebab0e62afd9e13866ef9b24a4390a3f27135afe Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 18 Jun 2014 19:02:46 +0530 Subject: [PATCH 263/294] CLI: Updated example.py to exit if drone has no ports --- binding/example.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/binding/example.py b/binding/example.py index 9ee39ad..6694f17 100644 --- a/binding/example.py +++ b/binding/example.py @@ -70,6 +70,10 @@ try: log.info('retreiving port config for all ports') port_config_list = drone.getPortConfig(port_id_list) + if len(port_config_list.port) == 0: + log.warning('drone has no ports!') + sys.exit(1) + # print port list and get tx/rx port id print('Port List') print('---------') From 4b19d8e1e739674cef0b8e3bd186c0e89399e17e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 21 Jun 2014 19:55:55 +0530 Subject: [PATCH 264/294] start/stop Transmit/Capture return only after transmit/capture has been started/stopped. Removed sleep between startCapture and startTx from example.py --- binding/example.py | 1 - server/pcapport.cpp | 55 +++++++++++++++++++++++++++++++++++++++------ server/pcapport.h | 17 ++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/binding/example.py b/binding/example.py index 6694f17..28de5c5 100644 --- a/binding/example.py +++ b/binding/example.py @@ -145,7 +145,6 @@ try: # start capture and transmit log.info('starting capture') drone.startCapture(rx_port) - time.sleep(1) log.info('starting transmit') drone.startTx(tx_port) diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 94ac735..1105401 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -324,6 +324,7 @@ PcapPort::PortTransmitter::PortTransmitter(const char *device) Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", "This Win32 platform does not support performance counter"); #endif + state_ = kNotStarted; returnToQIdx_ = -1; loopDelay_ = 0; stop_ = false; @@ -490,7 +491,7 @@ void PcapPort::PortTransmitter::run() qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); if (packetSequenceList_.size() <= 0) - return; + goto _exit; for(i = 0; i < packetSequenceList_.size(); i++) { qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, @@ -502,6 +503,7 @@ void PcapPort::PortTransmitter::run() packetSequenceList_.at(i)->usecDuration_); } + state_ = kRunning; i = 0; while (i < packetSequenceList_.size()) { @@ -563,7 +565,7 @@ _restart: qDebug("error %d in sendQueueTransmit()", ret); qDebug("overHead = %ld", overHead); stop_ = false; - return; + goto _exit; } } } @@ -587,12 +589,31 @@ _restart: i = returnToQIdx_; goto _restart; } + +_exit: + state_ = kFinished; +} + +void PcapPort::PortTransmitter::start() +{ + // FIXME: return error + if (state_ == kRunning) + return; + + state_ = kNotStarted; + QThread::start(); + + while (state_ == kNotStarted) + QThread::msleep(10); } void PcapPort::PortTransmitter::stop() { - if (isRunning()) + if (state_ == kRunning) { stop_ = true; + while (state_ == kRunning) + QThread::msleep(10); + } } int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, @@ -692,6 +713,7 @@ PcapPort::PortCapturer::PortCapturer(const char *device) { device_ = QString::fromAscii(device); stop_ = false; + state_ = kNotStarted; if (!capFile_.open()) qWarning("Unable to open temp cap file"); @@ -717,7 +739,7 @@ void PcapPort::PortCapturer::run() if (!capFile_.isOpen()) { qWarning("temp cap file is not open"); - return; + goto _exit; } _retry: handle_ = pcap_open_live(device_.toAscii().constData(), 65535, @@ -736,13 +758,13 @@ _retry: { qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device_.toAscii().constData(), errbuf); - return; + goto _exit; } } dumpHandle_ = pcap_dump_open(handle_, capFile_.fileName().toAscii().constData()); - + state_ = kRunning; while (1) { int ret; @@ -778,12 +800,31 @@ _retry: dumpHandle_ = NULL; handle_ = NULL; stop_ = false; + +_exit: + state_ = kFinished; +} + +void PcapPort::PortCapturer::start() +{ + // FIXME: return error + if (state_ == kRunning) + return; + + state_ = kNotStarted; + QThread::start(); + + while (state_ == kNotStarted) + QThread::msleep(10); } void PcapPort::PortCapturer::stop() { - if (isRunning()) + if (state_ == kRunning) { stop_ = true; + while (state_ == kRunning) + QThread::msleep(10); + } } QFile* PcapPort::PortCapturer::captureFile() diff --git a/server/pcapport.h b/server/pcapport.h index d05018b..5883c43 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -114,8 +114,15 @@ protected: void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); void run(); + void start(); void stop(); private: + enum State + { + kNotStarted, + kRunning, + kFinished + }; class PacketSequence { @@ -183,6 +190,7 @@ protected: bool usingInternalHandle_; pcap_t *handle_; volatile bool stop_; + volatile State state_; }; class PortCapturer: public QThread @@ -191,15 +199,24 @@ protected: PortCapturer(const char *device); ~PortCapturer(); void run(); + void start(); void stop(); QFile* captureFile(); private: + enum State + { + kNotStarted, + kRunning, + kFinished + }; + QString device_; volatile bool stop_; QTemporaryFile capFile_; pcap_t *handle_; pcap_dumper_t *dumpHandle_; + volatile State state_; }; PortMonitor *monitorRx_; From 584362406ea7f09c0358f093b9f3efd0f81a0e57 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 22 Jun 2014 10:43:54 +0530 Subject: [PATCH 265/294] Fixed a nasty drone crash caused by incorrect usage of qPrintable() in a QtMsgHandler --- rpc/rpcconn.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 5d9ac4d..9cdbd16 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -307,7 +307,9 @@ void RpcConnection::connIdMsgHandler(QtMsgType type, const char* msg) QString newMsg(*connId.localData()); newMsg.append(msg); newMsg.replace(QChar('\n'), QString("\n").append(*connId.localData())); - msg = qPrintable(newMsg); + fprintf(stderr, "%s\n", qPrintable(newMsg)); + fflush(stderr); + return; } fprintf(stderr, "%s\n", msg); From 42a23b12edc56541dd50d9d2245b395e0094bbe9 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 29 Jun 2014 20:07:01 +0530 Subject: [PATCH 266/294] Added a Error Msg Type to underlying RPC infra; RPC service now returns error for add/modify/delete stream if transmit is running; added prints for start/stop transmit/capture NOP cases; added a rpctest.py script --- binding/rpc.py | 5 +- rpc/pbrpcchannel.cpp | 67 +++++-- rpc/pbrpccommon.h | 1 + rpc/pbrpccontroller.h | 11 +- rpc/rpcconn.cpp | 27 ++- rpc/rpcconn.h | 2 + server/myservice.cpp | 22 +++ server/pcapport.cpp | 28 ++- server/pcapport.h | 2 + test/rpctest.py | 412 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 548 insertions(+), 29 deletions(-) create mode 100644 test/rpctest.py diff --git a/binding/rpc.py b/binding/rpc.py index aea0a53..2078bb3 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -74,6 +74,7 @@ class OstinatoRpcChannel(RpcChannel): MSG_TYPE_REQUEST = 1 MSG_TYPE_RESPONSE = 2 MSG_TYPE_BLOB = 3 + MSG_TYPE_ERROR = 4 error = '' try: @@ -118,6 +119,8 @@ class OstinatoRpcChannel(RpcChannel): self.log.debug('parsed response %s', response) elif msg_type == MSG_TYPE_BLOB: response = resp + elif msg_type == MSG_TYPE_ERROR: + raise RpcError(unicode(resp, 'utf-8')) else: raise RpcError('unknown RPC msg type %d' % msg_type) @@ -152,7 +155,7 @@ class OstinatoRpcChannel(RpcChannel): self.log.exception(error) raise except RpcError as e: - error = 'ERROR: Unknown reply received for RPC %s() (%s) ' % ( + error = 'ERROR: error received for RPC %s() (%s) ' % ( method.name, e) self.log.exception(error) raise diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index 5ef1003..f1fec25 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -22,6 +22,8 @@ along with this program. If not, see #include +static uchar msgBuf[4096]; + PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) { isPending = false; @@ -94,8 +96,7 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char msgBuf[PB_HDR_SIZE]; - char* const msg = &msgBuf[0]; + char* const msg = (char*) &msgBuf[0]; int len; bool ret; @@ -161,8 +162,7 @@ void PbRpcChannel::CallMethod( void PbRpcChannel::on_mpSocket_readyRead() { - uchar msg[PB_HDR_SIZE]; - uchar *p = (uchar*) &msg; + uchar *msg = (uchar*) &msgBuf; int msgLen; static bool parsing = false; static quint16 type, method; @@ -183,9 +183,9 @@ void PbRpcChannel::on_mpSocket_readyRead() Q_ASSERT(msgLen == PB_HDR_SIZE); - type = qFromBigEndian(p+0); - method = qFromBigEndian(p+2); - len = qFromBigEndian(p+4); + type = qFromBigEndian(msg+0); + method = qFromBigEndian(msg+2); + len = qFromBigEndian(msg+4); //BUFDUMP(msg, PB_HDR_SIZE); //qDebug("type = %hu, method = %hu, len = %u", type, method, len); @@ -207,8 +207,8 @@ void PbRpcChannel::on_mpSocket_readyRead() { int l; - l = mpSocket->read((char*)msg, sizeof(msg)); - blob->write((char*)msg, l); + l = mpSocket->read((char*)msgBuf, sizeof(msgBuf)); + blob->write((char*)msgBuf, l); cumLen += l; } @@ -221,13 +221,13 @@ void PbRpcChannel::on_mpSocket_readyRead() if (!isPending) { - qDebug("not waiting for response"); + qWarning("not waiting for response"); goto _error_exit2; } if (pendingMethodId != method) { - qDebug("invalid method id %d (expected = %d)", method, + qWarning("invalid method id %d (expected = %d)", method, pendingMethodId); goto _error_exit2; } @@ -241,13 +241,13 @@ void PbRpcChannel::on_mpSocket_readyRead() if (!isPending) { - qDebug("not waiting for response"); + qWarning("not waiting for response"); goto _error_exit; } if (pendingMethodId != method) { - qDebug("invalid method id %d (expected = %d)", method, + qWarning("invalid method id %d (expected = %d)", method, pendingMethodId); goto _error_exit; } @@ -274,6 +274,47 @@ void PbRpcChannel::on_mpSocket_readyRead() } break; + case PB_MSG_TYPE_ERROR: + { + static quint32 cumLen = 0; + static QByteArray error; + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msgBuf, sizeof(msgBuf)); + error.append(QByteArray((char*)msgBuf,l)); + cumLen += l; + } + + qDebug("%s: error rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + static_cast(controller)->SetFailed( + QString::fromUtf8(error, len)); + + cumLen = 0; + error.resize(0); + + if (!isPending) + { + qWarning("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qWarning("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + default: qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); goto _error_exit; diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h index e1fbdf9..07c8013 100644 --- a/rpc/pbrpccommon.h +++ b/rpc/pbrpccommon.h @@ -35,5 +35,6 @@ along with this program. If not, see #define PB_MSG_TYPE_REQUEST 1 #define PB_MSG_TYPE_RESPONSE 2 #define PB_MSG_TYPE_BINBLOB 3 +#define PB_MSG_TYPE_ERROR 4 #endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index 88782e7..af9c292 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -44,14 +44,17 @@ public: ::google::protobuf::Message* response() { return response_; } // Client Side Methods - void Reset() { failed = false; blob = NULL; } + void Reset() { failed = false; blob = NULL; errStr = ""; } bool Failed() const { return failed; } void StartCancel() { /*! \todo (MED) */} - std::string ErrorText() const { return errStr; } + std::string ErrorText() const { return errStr.toStdString(); } // Server Side Methods + void SetFailed(const QString &reason) + { failed = true; errStr = reason; qWarning(qPrintable(errStr)); } void SetFailed(const std::string &reason) - { failed = true; errStr = reason; } + { SetFailed(QString::fromStdString(reason)); } + QString ErrorString() const { return errStr; } bool IsCanceled() const { return false; }; void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { /*! \todo (MED) */ @@ -64,7 +67,7 @@ public: private: bool failed; QIODevice *blob; - std::string errStr; + QString errStr; ::google::protobuf::Message *request_; ::google::protobuf::Message *response_; diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 9cdbd16..7273df7 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -101,6 +101,14 @@ void RpcConnection::start() this, SLOT(on_clientSock_error(QAbstractSocket::SocketError))); } +void RpcConnection::writeHeader(char* header, quint16 type, quint16 method, + quint32 length) +{ + *((quint16*)(header+0)) = qToBigEndian(type); + *((quint16*)(header+2)) = qToBigEndian(method); + *((quint32*)(header+4)) = qToBigEndian(length); +} + void RpcConnection::sendRpcReply(PbRpcController *controller) { google::protobuf::Message *response = controller->response(); @@ -111,7 +119,14 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) if (controller->Failed()) { - qDebug("rpc failed"); + QByteArray err = controller->ErrorString().toUtf8(); + + qWarning("rpc failed (%s)", qPrintable(controller->ErrorString())); + len = err.size(); + writeHeader(msg, PB_MSG_TYPE_ERROR, pendingMethodId, len); + clientSock->write(msg, PB_HDR_SIZE); + clientSock->write(err.constData(), len); + goto _exit; } @@ -121,10 +136,7 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) len = blob->size(); qDebug("is binary blob of len %d", len); - *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_BINBLOB)); // type - *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method - (*(quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len - + writeHeader(msg, PB_MSG_TYPE_BINBLOB, pendingMethodId, len); clientSock->write(msg, PB_HDR_SIZE); blob->seek(0); @@ -152,10 +164,7 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) } len = response->ByteSize(); - - *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_RESPONSE)); // type - *((quint16*)(msg+2)) = qToBigEndian(quint16(pendingMethodId)); // method - *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + writeHeader(msg, PB_MSG_TYPE_RESPONSE, pendingMethodId, len); // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 13) diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h index fb23bb8..7ea581e 100644 --- a/rpc/rpcconn.h +++ b/rpc/rpcconn.h @@ -45,6 +45,8 @@ public: static void connIdMsgHandler(QtMsgType type, const char* msg); private: + void writeHeader(char* header, quint16 type, quint16 method, + quint32 length); void sendRpcReply(PbRpcController *controller); signals: diff --git a/server/myservice.cpp b/server/myservice.cpp index 5a591aa..1696cba 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -208,6 +208,9 @@ void MyService::addStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) { @@ -232,8 +235,13 @@ void MyService::addStream(::google::protobuf::RpcController* controller, done->Run(); return; +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; + _invalid_port: controller->SetFailed("invalid portid"); +_exit: done->Run(); } @@ -250,6 +258,9 @@ void MyService::deleteStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) portInfo[portId]->deleteStream(request->stream_id(i).id()); @@ -260,8 +271,12 @@ void MyService::deleteStream(::google::protobuf::RpcController* controller, done->Run(); return; +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; _invalid_port: controller->SetFailed("invalid portid"); +_exit: done->Run(); } @@ -278,6 +293,9 @@ void MyService::modifyStream(::google::protobuf::RpcController* controller, if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_size(); i++) { @@ -300,8 +318,12 @@ void MyService::modifyStream(::google::protobuf::RpcController* controller, done->Run(); return; +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; _invalid_port: controller->SetFailed("invalid portid"); +_exit: done->Run(); } diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 1105401..8b500f2 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -597,8 +597,10 @@ _exit: void PcapPort::PortTransmitter::start() { // FIXME: return error - if (state_ == kRunning) + if (state_ == kRunning) { + qWarning("Transmit start requested but is already running!"); return; + } state_ = kNotStarted; QThread::start(); @@ -614,6 +616,16 @@ void PcapPort::PortTransmitter::stop() while (state_ == kRunning) QThread::msleep(10); } + else { + // FIXME: return error + qWarning("Transmit stop requested but is not running!"); + return; + } +} + +bool PcapPort::PortTransmitter::isRunning() +{ + return (state_ == kRunning); } int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, @@ -808,8 +820,10 @@ _exit: void PcapPort::PortCapturer::start() { // FIXME: return error - if (state_ == kRunning) + if (state_ == kRunning) { + qWarning("Capture start requested but is already running!"); return; + } state_ = kNotStarted; QThread::start(); @@ -825,6 +839,16 @@ void PcapPort::PortCapturer::stop() while (state_ == kRunning) QThread::msleep(10); } + else { + // FIXME: return error + qWarning("Capture stop requested but is not running!"); + return; + } +} + +bool PcapPort::PortCapturer::isRunning() +{ + return (state_ == kRunning); } QFile* PcapPort::PortCapturer::captureFile() diff --git a/server/pcapport.h b/server/pcapport.h index 5883c43..b25ab5c 100644 --- a/server/pcapport.h +++ b/server/pcapport.h @@ -116,6 +116,7 @@ protected: void run(); void start(); void stop(); + bool isRunning(); private: enum State { @@ -201,6 +202,7 @@ protected: void run(); void start(); void stop(); + bool isRunning(); QFile* captureFile(); private: diff --git a/test/rpctest.py b/test/rpctest.py new file mode 100644 index 0000000..759004c --- /dev/null +++ b/test/rpctest.py @@ -0,0 +1,412 @@ +#! /usr/bin/env python + +# standard modules +import logging +import os +import subprocess +import sys +import time + +sys.path.append('../binding') +from core import ost_pb, DroneProxy +from rpc import RpcError +from protocols.mac_pb2 import mac +from protocols.ip4_pb2 import ip4, Ip4 + +class Test: + pass + +class TestSuite: + def __init__(self): + self.results = [] + self.total = 0 + self.passed = 0 + self.completed = False + + def test_begin(self, name): + test = Test() + test.name = name + test.passed = False + self.running = test + print('-----------------------------------------------------------') + print('@@TEST: %s' % name) + print('-----------------------------------------------------------') + + def test_end(self, result): + if self.running: + self.running.passed = result + self.results.append(self.running) + self.total = self.total + 1 + if result: + self.passed = self.passed + 1 + self.running = None + print('@@RESULT: %s' % ('PASS' if result else 'FAIL')) + else: + raise Exception('Test end without a test begin') + + def report(self): + print('===========================================================') + print('TEST REPORT') + print('===========================================================') + for test in self.results: + print('%s: %d' % (test.name, test.passed)) + print('Passed: %d/%d' % (self.passed, self.total)) + print('Completed: %d' % (self.completed)) + + def complete(self): + self.completed = True + + def passed(self): + return passed == total and self.completed + +# initialize defaults +host_name = '127.0.0.1' +tx_port_number = -1 +rx_port_number = -1 + +if sys.platform == 'win32': + tshark = r'C:\Program Files\Wireshark\tshark.exe' +else: + tshark = 'tshark' + + +# setup logging +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +print('') +print('This test uses the following topology -') +print('') +print(' +-------+ ') +print(' | |Tx--->----+') +print(' | Drone | |') +print(' | |Rx---<----+') +print(' +-------+ ') +print('') +print('A loopback port is used as both the Tx and Rx ports') +print('') + +suite = TestSuite() +drone = DroneProxy(host_name) + +try: + # ----------------------------------------------------------------- # + # Baseline Configuration + # ----------------------------------------------------------------- # + + # connect to drone + log.info('connecting to drone(%s:%d)' + % (drone.hostName(), drone.portNumber())) + drone.connect() + + # retreive port id list + log.info('retreiving port list') + port_id_list = drone.getPortIdList() + + # retreive port config list + log.info('retreiving port config for all ports') + port_config_list = drone.getPortConfig(port_id_list) + + if len(port_config_list.port) == 0: + log.warning('drone has no ports!') + sys.exit(1) + + # iterate port list to find a loopback port to use as the tx/rx port id + print('Port List') + print('---------') + for port in port_config_list.port: + print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) + # use a loopback port as default tx/rx port + if ('lo' in port.name or 'loopback' in port.description.lower()): + tx_port_number = port.port_id.id + rx_port_number = port.port_id.id + + if tx_port_number < 0 or rx_port_number < 0: + log.warning('loopback port not found') + sys.exit(1) + + print('Using port %d as tx/rx port(s)') + + tx_port = ost_pb.PortIdList() + tx_port.port_id.add().id = tx_port_number; + + rx_port = ost_pb.PortIdList() + rx_port.port_id.add().id = rx_port_number; + + # add a stream + stream_id = ost_pb.StreamIdList() + stream_id.port_id.CopyFrom(tx_port.port_id[0]) + stream_id.stream_id.add().id = 1 + log.info('adding tx_stream %d' % stream_id.stream_id[0].id) + drone.addStream(stream_id) + + # configure the stream + stream_cfg = ost_pb.StreamConfigList() + stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) + s = stream_cfg.stream.add() + s.stream_id.id = stream_id.stream_id[0].id + s.core.is_enabled = True + s.control.num_packets = 10 + + # setup stream protocols as mac:eth2:ip4:udp:payload + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber + p.Extensions[mac].dst_mac = 0x001122334455 + p.Extensions[mac].src_mac = 0x00aabbccddee + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber + # reduce typing by creating a shorter reference to p.Extensions[ip4] + ip = p.Extensions[ip4] + ip.src_ip = 0x01020304 + ip.dst_ip = 0x05060708 + ip.dst_ip_mode = Ip4.e_im_inc_host + + s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + + log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) + drone.modifyStream(stream_cfg) + + # clear tx/rx stats + log.info('clearing tx/rx stats') + drone.clearStats(tx_port) + drone.clearStats(rx_port) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking addStream() during transmit fails + # TESTCASE: Verify invoking modifyStream() during transmit fails + # TESTCASE: Verify invoking deleteStream() during transmit fails + # ----------------------------------------------------------------- # + sid = ost_pb.StreamIdList() + sid.port_id.CopyFrom(tx_port.port_id[0]) + sid.stream_id.add().id = 2 + + passed = False + suite.test_begin('addStreamDuringTransmitFails') + drone.startTx(tx_port) + try: + log.info('adding tx_stream %d' % sid.stream_id[0].id) + drone.addStream(sid) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + passed = False + suite.test_begin('modifyStreamDuringTransmitFails') + scfg = ost_pb.StreamConfigList() + scfg.port_id.CopyFrom(tx_port.port_id[0]) + s = scfg.stream.add() + s.stream_id.id = sid.stream_id[0].id + s.protocol.add().protocol_id.id = ost_pb.Protocol.kMacFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kArpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + drone.startTx(tx_port) + try: + log.info('configuring tx_stream %d' % sid.stream_id[0].id) + drone.modifyStream(scfg) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + passed = False + suite.test_begin('deleteStreamDuringTransmitFails') + drone.startTx(tx_port) + try: + log.info('deleting tx_stream %d' % sid.stream_id[0].id) + drone.deleteStream(sid) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking startTx() during transmit is a NOP, + # not a restart + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startTxDuringTransmitIsNopNotRestart') + drone.startCapture(rx_port) + drone.startTx(tx_port) + try: + log.info('sleeping for 4s ...') + time.sleep(4) + log.info('starting transmit multiple times') + drone.startTx(tx_port) + time.sleep(1) + drone.startTx(tx_port) + time.sleep(1) + drone.startTx(tx_port) + time.sleep(1) + log.info('waiting for transmit to finish ...') + time.sleep(5) + drone.stopTx(tx_port) + drone.stopCapture(rx_port) + + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking startCapture() during capture is a NOP, + # not a restart + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startCaptureDuringTransmitIsNopNotRestart') + try: + drone.startCapture(rx_port) + drone.startTx(tx_port) + log.info('sleeping for 4s ...') + time.sleep(4) + log.info('starting capture multiple times') + drone.startCapture(rx_port) + time.sleep(1) + drone.startCapture(rx_port) + time.sleep(1) + drone.startCapture(rx_port) + time.sleep(1) + log.info('waiting for transmit to finish ...') + time.sleep(5) + drone.stopTx(tx_port) + drone.stopCapture(rx_port) + + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking stopTx() when transmit is not running + # is a NOP + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('stopTxWhenTransmitNotRunningIsNop') + try: + tx_stats = drone.getStats(tx_port) + log.info('--> (tx_stats)' + tx_stats.__str__()) + if tx_stats.port_stats[0].state.is_transmit_on: + raise Exception('Unexpected transmit ON state') + log.info('stopping transmit multiple times') + drone.stopTx(tx_port) + time.sleep(1) + drone.stopTx(tx_port) + time.sleep(1) + drone.stopTx(tx_port) + + # if we reached here, that means there was no exception + passed = True + except RpcError as e: + raise + finally: + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking stopCapture() when capture is not running + # is a NOP + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('stopCaptureWhenCaptureNotRunningIsNop') + try: + rx_stats = drone.getStats(rx_port) + log.info('--> (rx_stats)' + rx_stats.__str__()) + if rx_stats.port_stats[0].state.is_capture_on: + raise Exception('Unexpected capture ON state') + log.info('stopping capture multiple times') + drone.stopCapture(rx_port) + time.sleep(1) + drone.stopCapture(rx_port) + time.sleep(1) + drone.stopCapture(rx_port) + + # if we reached here, that means there was no exception + passed = True + except RpcError as e: + raise + finally: + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify startCapture(), startTx() sequence captures the + # first packet + # TESTCASE: Verify stopTx(), stopCapture() sequence captures the + # last packet + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startStopTransmitCaptureOrderCapturesAllPackets') + try: + drone.startCapture(rx_port) + drone.startTx(tx_port) + log.info('waiting for transmit to finish ...') + time.sleep(12) + drone.stopTx(tx_port) + drone.stopCapture(rx_port) + + log.info('getting Rx capture buffer') + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts and '5.6.7.17' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTx(tx_port) + suite.test_end(passed) + + suite.complete() + + # delete streams + log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) + drone.deleteStream(stream_id) + + # bye for now + drone.disconnect() + +except Exception as ex: + log.exception(ex) + +finally: + suite.report() + if not suite.passed: + sys.exit(2); From 7bfb14628436530b20096112ba46ac9dbf7fee79 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 29 Jun 2014 20:31:34 +0530 Subject: [PATCH 267/294] Fixed StreamConfigDialog Tab Order --- client/streamconfigdialog.ui | 70 ++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index be71e47..4b3a066 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -1213,54 +1213,70 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff lePktLen lePktLenMin lePktLenMax + rbL1None + rbL1Mac + rbL1Other + rbVlanNone + rbVlanSingle + rbVlanDouble + rbFtNone rbFtEthernet2 rbFt802Dot3Raw rbFt802Dot3Llc rbFtLlcSnap rbFtOther - rbVlanNone - rbVlanSingle - rbVlanDouble rbL3None + rbL3Arp rbL3Ipv4 rbL3Ipv6 - rbL3Arp + rbL3Ip6over4 + rbL3Ip4over6 + rbL3Ip4over4 + rbL3Ip6over6 rbL3Other rbL4None rbL4Icmp rbL4Igmp + rbL4Mld rbL4Tcp rbL4Udp rbL4Other + rbL5None + rbL5Text + rbL5Other + rbPayloadNone rbPayloadPattern + rbPayloadHexDump rbPayloadOther - pbPrev - pbNext - pbOk - pbCancel - rbSendBursts - leNumPackets - leNumBursts - lePacketsPerBurst - rbActionStop - rbActionGotoNext - rbActionGotoStream - leStreamId - rbModeFixed - rbModeContinuous - lePacketsPerSec - leBurstsPerSec - leGapIsg - leGapIpg - leGapIbg - tvPacketTree + lvAllProtocols + tbAdd + tbUp tbDown tbDelete lvSelectedProtocols rbSendPackets - tbUp - lvAllProtocols - tbAdd + rbSendBursts + rbModeFixed + rbModeContinuous + leNumPackets + leNumBursts + lePacketsPerBurst + lePacketsPerSec + leBurstsPerSec + rbBitsPerSec + leBitsPerSec + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + leGapIsg + leGapIbg + leGapIpg + tvPacketTree + pbPrev + pbNext + pbOk + pbCancel From 3b546637e4aed3d9e272ade0a60dc15dea207504 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 29 Jun 2014 21:04:19 +0530 Subject: [PATCH 268/294] Updated About Dialog with copyright date and web URLs --- client/about.ui | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/client/about.ui b/client/about.ui index 4727324..93354ea 100644 --- a/client/about.ui +++ b/client/about.ui @@ -92,15 +92,31 @@ - + - Copyright (c) 2007-2012 Srivats P. + Copyright (c) 2007-2014 Srivats P. Qt::AlignCenter + + + + <a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + @@ -150,6 +166,9 @@ Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/)
    true + + true + From 1ea635bcfbfabb493e23a82e3fcfb1198773314b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 30 Jun 2014 19:49:17 +0530 Subject: [PATCH 269/294] Renamed RPCs start/stopTx as start/stopTransmit --- binding/example.py | 4 ++-- client/portgroup.cpp | 4 ++-- common/protocol.proto | 4 ++-- server/myservice.cpp | 4 ++-- server/myservice.h | 4 ++-- test/rpctest.py | 54 +++++++++++++++++++++---------------------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/binding/example.py b/binding/example.py index 28de5c5..0a77a27 100644 --- a/binding/example.py +++ b/binding/example.py @@ -146,7 +146,7 @@ try: log.info('starting capture') drone.startCapture(rx_port) log.info('starting transmit') - drone.startTx(tx_port) + drone.startTransmit(tx_port) # wait for transmit to finish log.info('waiting for transmit to finish ...') @@ -154,7 +154,7 @@ try: # stop transmit and capture log.info('stopping transmit') - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) log.info('stopping capture') drone.stopCapture(rx_port) diff --git a/client/portgroup.cpp b/client/portgroup.cpp index 0d50228..e411652 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -574,7 +574,7 @@ void PortGroup::startTx(QList *portList) portId->set_id(portList->at(i)); } - serviceStub->startTx(controller, portIdList, ack, + serviceStub->startTransmit(controller, portIdList, ack, NewCallback(this, &PortGroup::processStartTxAck, controller)); } _exit: @@ -609,7 +609,7 @@ void PortGroup::stopTx(QList *portList) portId->set_id(portList->at(i)); } - serviceStub->stopTx(controller, portIdList, ack, + serviceStub->stopTransmit(controller, portIdList, ack, NewCallback(this, &PortGroup::processStopTxAck, controller)); } _exit: diff --git a/common/protocol.proto b/common/protocol.proto index 07012b5..ad9477a 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -237,8 +237,8 @@ service OstService { rpc deleteStream(StreamIdList) returns (Ack); rpc modifyStream(StreamConfigList) returns (Ack); - rpc startTx(PortIdList) returns (Ack); - rpc stopTx(PortIdList) returns (Ack); + rpc startTransmit(PortIdList) returns (Ack); + rpc stopTransmit(PortIdList) returns (Ack); rpc startCapture(PortIdList) returns (Ack); rpc stopCapture(PortIdList) returns (Ack); diff --git a/server/myservice.cpp b/server/myservice.cpp index 1696cba..825ab2c 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -327,7 +327,7 @@ _exit: done->Run(); } -void MyService::startTx(::google::protobuf::RpcController* /*controller*/, +void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) @@ -352,7 +352,7 @@ void MyService::startTx(::google::protobuf::RpcController* /*controller*/, done->Run(); } -void MyService::stopTx(::google::protobuf::RpcController* /*controller*/, +void MyService::stopTransmit(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) diff --git a/server/myservice.h b/server/myservice.h index 3508dc4..557a30f 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -69,11 +69,11 @@ public: const ::OstProto::StreamConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); - virtual void startTx(::google::protobuf::RpcController* controller, + virtual void startTransmit(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); - virtual void stopTx(::google::protobuf::RpcController* controller, + virtual void stopTransmit(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); diff --git a/test/rpctest.py b/test/rpctest.py index 759004c..cbb3f13 100644 --- a/test/rpctest.py +++ b/test/rpctest.py @@ -187,7 +187,7 @@ try: passed = False suite.test_begin('addStreamDuringTransmitFails') - drone.startTx(tx_port) + drone.startTransmit(tx_port) try: log.info('adding tx_stream %d' % sid.stream_id[0].id) drone.addStream(sid) @@ -197,7 +197,7 @@ try: else: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) passed = False @@ -209,7 +209,7 @@ try: s.protocol.add().protocol_id.id = ost_pb.Protocol.kMacFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kArpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber - drone.startTx(tx_port) + drone.startTransmit(tx_port) try: log.info('configuring tx_stream %d' % sid.stream_id[0].id) drone.modifyStream(scfg) @@ -219,12 +219,12 @@ try: else: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) passed = False suite.test_begin('deleteStreamDuringTransmitFails') - drone.startTx(tx_port) + drone.startTransmit(tx_port) try: log.info('deleting tx_stream %d' % sid.stream_id[0].id) drone.deleteStream(sid) @@ -234,31 +234,31 @@ try: else: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # - # TESTCASE: Verify invoking startTx() during transmit is a NOP, + # TESTCASE: Verify invoking startTransmit() during transmit is a NOP, # not a restart # ----------------------------------------------------------------- # passed = False - suite.test_begin('startTxDuringTransmitIsNopNotRestart') + suite.test_begin('startTransmitDuringTransmitIsNopNotRestart') drone.startCapture(rx_port) - drone.startTx(tx_port) + drone.startTransmit(tx_port) try: log.info('sleeping for 4s ...') time.sleep(4) log.info('starting transmit multiple times') - drone.startTx(tx_port) + drone.startTransmit(tx_port) time.sleep(1) - drone.startTx(tx_port) + drone.startTransmit(tx_port) time.sleep(1) - drone.startTx(tx_port) + drone.startTransmit(tx_port) time.sleep(1) log.info('waiting for transmit to finish ...') time.sleep(5) - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) drone.stopCapture(rx_port) buff = drone.getCaptureBuffer(rx_port.port_id[0]) @@ -272,7 +272,7 @@ try: except RpcError as e: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) @@ -284,7 +284,7 @@ try: suite.test_begin('startCaptureDuringTransmitIsNopNotRestart') try: drone.startCapture(rx_port) - drone.startTx(tx_port) + drone.startTransmit(tx_port) log.info('sleeping for 4s ...') time.sleep(4) log.info('starting capture multiple times') @@ -296,7 +296,7 @@ try: time.sleep(1) log.info('waiting for transmit to finish ...') time.sleep(5) - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) drone.stopCapture(rx_port) buff = drone.getCaptureBuffer(rx_port.port_id[0]) @@ -310,26 +310,26 @@ try: except RpcError as e: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # - # TESTCASE: Verify invoking stopTx() when transmit is not running + # TESTCASE: Verify invoking stopTransmit() when transmit is not running # is a NOP # ----------------------------------------------------------------- # passed = False - suite.test_begin('stopTxWhenTransmitNotRunningIsNop') + suite.test_begin('stopTransmitWhenTransmitNotRunningIsNop') try: tx_stats = drone.getStats(tx_port) log.info('--> (tx_stats)' + tx_stats.__str__()) if tx_stats.port_stats[0].state.is_transmit_on: raise Exception('Unexpected transmit ON state') log.info('stopping transmit multiple times') - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) time.sleep(1) - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) time.sleep(1) - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) # if we reached here, that means there was no exception passed = True @@ -364,19 +364,19 @@ try: suite.test_end(passed) # ----------------------------------------------------------------- # - # TESTCASE: Verify startCapture(), startTx() sequence captures the + # TESTCASE: Verify startCapture(), startTransmit() sequence captures the # first packet - # TESTCASE: Verify stopTx(), stopCapture() sequence captures the + # TESTCASE: Verify stopTransmit(), stopCapture() sequence captures the # last packet # ----------------------------------------------------------------- # passed = False suite.test_begin('startStopTransmitCaptureOrderCapturesAllPackets') try: drone.startCapture(rx_port) - drone.startTx(tx_port) + drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(12) - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') @@ -391,7 +391,7 @@ try: except RpcError as e: raise finally: - drone.stopTx(tx_port) + drone.stopTransmit(tx_port) suite.test_end(passed) suite.complete() From 048777c064afeaa42b0629409d3c217bccdd20da Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 30 Jun 2014 20:25:39 +0530 Subject: [PATCH 270/294] IGMPv3/MLDv2 Query widget now displays the Group Address field Fixes issue 107 --- common/gmp.cpp | 8 ++++---- common/igmpconfig.cpp | 2 +- common/mldconfig.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/gmp.cpp b/common/gmp.cpp index 8d7b0fb..240650d 100755 --- a/common/gmp.cpp +++ b/common/gmp.cpp @@ -737,8 +737,8 @@ int GmpProtocol::protocolFrameSize(int streamIndex) const bool GmpProtocol::isProtocolFrameValueVariable() const { - // No fields vary for Ssm Query and Report - if (isSsmReport() || isSsmQuery()) + // No fields vary for Ssm Report + if (isSsmReport()) return false; // For all other msg types, check the group mode @@ -753,8 +753,8 @@ int GmpProtocol::protocolFrameVariableCount() const { int count = 1; - // No fields vary for Ssm Query and Report - if (isSsmReport() || isSsmQuery()) + // No fields vary for Ssm Report + if (isSsmReport()) return count; // For all other msg types, check the group mode diff --git a/common/igmpconfig.cpp b/common/igmpconfig.cpp index d7bdaa4..743e9a3 100644 --- a/common/igmpconfig.cpp +++ b/common/igmpconfig.cpp @@ -92,7 +92,7 @@ void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) break; case kIgmpV3Query: - asmGroup->hide(); + asmGroup->show(); ssmWidget->setCurrentIndex(kSsmQueryPage); ssmWidget->show(); break; diff --git a/common/mldconfig.cpp b/common/mldconfig.cpp index d6b7b4b..bc871c3 100644 --- a/common/mldconfig.cpp +++ b/common/mldconfig.cpp @@ -90,7 +90,7 @@ void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) break; case kMldV2Query: - asmGroup->hide(); + asmGroup->show(); ssmWidget->setCurrentIndex(kSsmQueryPage); ssmWidget->show(); break; From 9955d31b77ddf55ef33e3370050a58493b9ecab1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 11:19:22 +0530 Subject: [PATCH 271/294] Added RPC for version compatibility check between client and server; both GUI and DroneProxy call this as the first RPC before proceeding ahead; if client is inompatible, drone additionally closes the connection; drone won't handle any RPCs untiil version check is performed and client is found compatible; fixed DroneProxy bug which did not allow to connect again after disconnect; fixed RPC bug which failed to detect unsupported method id; RPC-server now returns RPC error in case of failure; added a bunch of RPC tests for version compat --- binding/core.py | 9 ++- binding/rpc.py | 2 +- client/portgroup.cpp | 82 ++++++++++++++++++++---- client/portgroup.h | 22 +++++-- common/protocol.proto | 15 +++++ rpc/pbrpccontroller.h | 14 +++- rpc/rpcconn.cpp | 50 +++++++++++++-- rpc/rpcconn.h | 2 + server/myservice.cpp | 48 ++++++++++++++ server/myservice.h | 4 ++ test/rpctest.py | 146 +++++++++++++++++++++++++++++++++++++++++- 11 files changed, 367 insertions(+), 27 deletions(-) diff --git a/binding/core.py b/binding/core.py index ac28707..d8de580 100644 --- a/binding/core.py +++ b/binding/core.py @@ -16,8 +16,9 @@ # along with this program. If not, see import os -from rpc import OstinatoRpcChannel, OstinatoRpcController +from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError import protocols.protocol_pb2 as ost_pb +from __init__ import __version__ class DroneProxy(object): @@ -41,6 +42,12 @@ class DroneProxy(object): def connect(self): self.channel.connect(self.host, self.port) + ver = ost_pb.VersionInfo() + ver.version = __version__ + compat = self.checkVersion(ver) + if compat.result == ost_pb.VersionCompatibility.kIncompatible: + raise RpcError('incompatible version %s (%s)' % + (ver.version, compat.notes)) def disconnect(self): self.channel.disconnect() diff --git a/binding/rpc.py b/binding/rpc.py index 2078bb3..2eb721b 100644 --- a/binding/rpc.py +++ b/binding/rpc.py @@ -52,12 +52,12 @@ class OstinatoRpcChannel(RpcChannel): def __init__(self): self.log = logging.getLogger(__name__) self.log.debug('opening socket') - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def connect(self, host, port): self.peer = '%s:%d' % (host, port) self.log.debug('connecting to %s', self.peer) try: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) except socket.error as e: error = 'ERROR: Unable to connect to Drone %s (%s)' % ( diff --git a/client/portgroup.cpp b/client/portgroup.cpp index e411652..8e20735 100644 --- a/client/portgroup.cpp +++ b/client/portgroup.cpp @@ -33,6 +33,7 @@ along with this program. If not, see using ::google::protobuf::NewCallback; extern QMainWindow *mainWindow; +extern char *version; quint32 PortGroup::mPortGroupAllocId = 0; @@ -47,6 +48,8 @@ PortGroup::PortGroup(QHostAddress ip, quint16 port) statsController = new PbRpcController(portIdList_, portStatsList_); isGetStatsPending_ = false; + compat = kUnknown; + reconnect = false; reconnectAfter = kMinReconnectWaitTime; reconnectTimer = new QTimer(this); @@ -112,19 +115,62 @@ void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) void PortGroup::on_rpcChannel_connected() { - OstProto::Void *void_ = new OstProto::Void; - OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::VersionInfo *verInfo = new OstProto::VersionInfo; + OstProto::VersionCompatibility *verCompat = + new OstProto::VersionCompatibility; qDebug("connected\n"); emit portGroupDataChanged(mPortGroupId); reconnectAfter = kMinReconnectWaitTime; - qDebug("requesting portlist ..."); + qDebug("requesting version check ..."); + verInfo->set_version(version); - PbRpcController *controller = new PbRpcController(void_, portIdList); - serviceStub->getPortIdList(controller, void_, portIdList, - NewCallback(this, &PortGroup::processPortIdList, controller)); + PbRpcController *controller = new PbRpcController(verInfo, verCompat); + serviceStub->checkVersion(controller, verInfo, verCompat, + NewCallback(this, &PortGroup::processVersionCompatibility, + controller)); +} + +void PortGroup::processVersionCompatibility(PbRpcController *controller) +{ + OstProto::VersionCompatibility *verCompat + = static_cast(controller->response()); + + Q_ASSERT(verCompat != NULL); + + qDebug("got version result ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _error_exit; + } + + if (verCompat->result() == OstProto::VersionCompatibility::kIncompatible) { + qWarning("incompatible version %s (%s)", version, + qPrintable(QString::fromStdString(verCompat->notes()))); + compat = kIncompatible; + emit portGroupDataChanged(mPortGroupId); + goto _error_exit; + } + + compat = kCompatible; + + { + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("requesting portlist ..."); + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); + } + +_error_exit: + delete controller; } void PortGroup::on_rpcChannel_disconnected() @@ -152,6 +198,9 @@ void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) qDebug("%s: error %d", __FUNCTION__, socketError); emit portGroupDataChanged(mPortGroupId); + if (socketError == QAbstractSocket::RemoteHostClosedError) + reconnect = false; + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) { @@ -188,7 +237,8 @@ void PortGroup::processPortIdList(PbRpcController *controller) if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _error_exit; } @@ -239,7 +289,8 @@ void PortGroup::processPortConfigList(PbRpcController *controller) if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _error_exit; } @@ -369,7 +420,8 @@ void PortGroup::processModifyPortAck(PbRpcController *controller) if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _exit; } @@ -400,7 +452,8 @@ void PortGroup::processUpdatedPortConfig(PbRpcController *controller) if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _exit; } @@ -450,7 +503,8 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _exit; } @@ -521,7 +575,8 @@ void PortGroup::processStreamConfigList(int portIndex, if (controller->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); goto _exit; } @@ -781,7 +836,8 @@ void PortGroup::processPortStatsList() if (statsController->Failed()) { - qDebug("%s: rpc failed", __FUNCTION__); + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(statsController->ErrorString())); goto _error_exit; } diff --git a/client/portgroup.h b/client/portgroup.h index b4d7580..ccb8db8 100644 --- a/client/portgroup.h +++ b/client/portgroup.h @@ -43,6 +43,7 @@ class PortGroup : public QObject { Q_OBJECT private: + enum { kIncompatible, kCompatible, kUnknown } compat; static quint32 mPortGroupAllocId; quint32 mPortGroupId; QString mUserAlias; // user defined @@ -69,9 +70,16 @@ public: quint16 port = DEFAULT_SERVER_PORT); ~PortGroup(); - void connectToHost() { reconnect = true; rpcChannel->establish(); } - void connectToHost(QHostAddress ip, quint16 port) - { reconnect = true; rpcChannel->establish(ip, port); } + void connectToHost() { + reconnect = true; + compat = kUnknown; + rpcChannel->establish(); + } + void connectToHost(QHostAddress ip, quint16 port) { + reconnect = true; + compat = kUnknown; + rpcChannel->establish(ip, port); + } void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } int numPorts() const { return mPorts.size(); } @@ -84,9 +92,13 @@ public: { return rpcChannel->serverAddress(); } quint16 serverPort() const { return rpcChannel->serverPort(); } - QAbstractSocket::SocketState state() const - { return rpcChannel->state(); } + QAbstractSocket::SocketState state() const { + if (compat == kIncompatible) + return QAbstractSocket::SocketState(-1); + return rpcChannel->state(); + } + void processVersionCompatibility(PbRpcController *controller); void processPortIdList(PbRpcController *controller); void processPortConfigList(PbRpcController *controller); diff --git a/common/protocol.proto b/common/protocol.proto index ad9477a..2039bb4 100644 --- a/common/protocol.proto +++ b/common/protocol.proto @@ -21,6 +21,19 @@ package OstProto; option cc_generic_services = true; option py_generic_services = true; +message VersionInfo { + required string version = 1; +} + +message VersionCompatibility { + enum Compatibility { + kIncompatible = 0; + kCompatible = 1; + } + required Compatibility result = 1; + optional string notes = 2; +} + message StreamId { required uint32 id = 1; } @@ -246,5 +259,7 @@ service OstService { rpc getStats(PortIdList) returns (PortStatsList); rpc clearStats(PortIdList) returns (Ack); + + rpc checkVersion(VersionInfo) returns (VersionCompatibility); } diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index af9c292..eff2324 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -44,7 +44,12 @@ public: ::google::protobuf::Message* response() { return response_; } // Client Side Methods - void Reset() { failed = false; blob = NULL; errStr = ""; } + void Reset() { + failed = false; + disconnect = false; + blob = NULL; + errStr = ""; + } bool Failed() const { return failed; } void StartCancel() { /*! \todo (MED) */} std::string ErrorText() const { return errStr.toStdString(); } @@ -59,6 +64,12 @@ public: void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { /*! \todo (MED) */ } + void TriggerDisconnect() { + disconnect = true; + } + bool Disconnect() const { + return disconnect; + } // srivatsp added QIODevice* binaryBlob() { return blob; }; @@ -66,6 +77,7 @@ public: private: bool failed; + bool disconnect; QIODevice *blob; QString errStr; ::google::protobuf::Message *request_; diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 7273df7..3d91c75 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -50,6 +50,8 @@ RpcConnection::RpcConnection(int socketDescriptor, isPending = false; pendingMethodId = -1; // don't care as long as isPending is false + + isCompatCheckDone = false; } RpcConnection::~RpcConnection() @@ -180,7 +182,13 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) response->SerializeToZeroCopyStream(outStream); outStream->Flush(); + if (pendingMethodId == 15) + isCompatCheckDone = true; + _exit: + if (controller->Disconnect()) + clientSock->disconnectFromHost(); + delete controller; isPending = false; } @@ -210,6 +218,7 @@ void RpcConnection::on_clientSock_dataAvail() const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; PbRpcController *controller; + QString error; // Do we have enough bytes for a msg header? // If yes, peek into the header and get msg length @@ -241,6 +250,23 @@ void RpcConnection::on_clientSock_dataAvail() { qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, type, PB_MSG_TYPE_REQUEST); + error = QString("unexpected msg type %1; expected %2") + .arg(type).arg(PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + // If RPC is not checkVersion, ensure compat check is already done + if (!isCompatCheckDone && method != 15) { + qDebug("server(%s): version compatibility check pending", + __FUNCTION__); + error = "version compatibility check pending"; + goto _error_exit; + } + + if (method >= service->GetDescriptor()->method_count()) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + error = QString("invalid RPC method %1").arg(method); goto _error_exit; } @@ -248,13 +274,18 @@ void RpcConnection::on_clientSock_dataAvail() if (!methodDesc) { qDebug("server(%s): invalid method id %d", __FUNCTION__, method); - goto _error_exit; //! \todo Return Error to client + error = QString("invalid RPC method %1").arg(method); + goto _error_exit; } if (isPending) { qDebug("server(%s): rpc pending, try again", __FUNCTION__); - goto _error_exit; //! \todo Return Error to client + error = QString("RPC %1() is pending; only one RPC allowed at a time; " + "try again!").arg(QString::fromStdString( + service->GetDescriptor()->method( + pendingMethodId)->name())); + goto _error_exit; } pendingMethodId = method; @@ -278,7 +309,11 @@ void RpcConnection::on_clientSock_dataAvail() "missing = \n%s----->", method, req->DebugString().c_str(), req->InitializationErrorString().c_str()); - qFatal("exiting"); + error = QString("RPC %1() missing required fields in request - %2") + .arg(QString::fromStdString( + service->GetDescriptor()->method( + pendingMethodId)->name()), + QString(req->InitializationErrorString().c_str())); delete req; delete resp; @@ -306,7 +341,14 @@ void RpcConnection::on_clientSock_dataAvail() _error_exit: inStream->Skip(len); _error_exit2: - qDebug("server(%s): discarding msg from client", __FUNCTION__); + qDebug("server(%s): return error %s for msg from client", __FUNCTION__, + qPrintable(error)); + pendingMethodId = method; + isPending = true; + controller = new PbRpcController(NULL, NULL); + controller->SetFailed(error); + controller->TriggerDisconnect(); + sendRpcReply(controller); return; } diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h index 7ea581e..e41d8e2 100644 --- a/rpc/rpcconn.h +++ b/rpc/rpcconn.h @@ -68,6 +68,8 @@ private: bool isPending; int pendingMethodId; + + bool isCompatCheckDone; }; #endif diff --git a/server/myservice.cpp b/server/myservice.cpp index 825ab2c..55f3bf3 100644 --- a/server/myservice.cpp +++ b/server/myservice.cpp @@ -33,6 +33,11 @@ along with this program. If not, see #include "../rpc/pbrpccontroller.h" #include "portmanager.h" +#include + + +extern char *version; + MyService::MyService() { PortManager *portManager = PortManager::instance(); @@ -531,3 +536,46 @@ void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, done->Run(); } + +void MyService::checkVersion(::google::protobuf::RpcController* controller, + const ::OstProto::VersionInfo* request, + ::OstProto::VersionCompatibility* response, + ::google::protobuf::Closure* done) +{ + QString myVersion(version); + QString clientVersion; + QStringList my, client; + + qDebug("In %s", __PRETTY_FUNCTION__); + + my = myVersion.split('.'); + + Q_ASSERT(my.size() >= 2); + + clientVersion = QString::fromStdString(request->version()); + client = clientVersion.split('.'); + + qDebug("client = %s, my = %s", + qPrintable(clientVersion), qPrintable(myVersion)); + + if (client.size() < 2) + goto _invalid_version; + + // Compare only major and minor numbers + if (client[0] == my[0] && client[1] == my[1]) { + response->set_result(OstProto::VersionCompatibility::kCompatible); + } + else { + response->set_result(OstProto::VersionCompatibility::kIncompatible); + response->set_notes(QString("Drone needs client version %1.%2.x") + .arg(my[0], my[1]).toStdString()); + static_cast(controller)->TriggerDisconnect(); + } + + done->Run(); + return; + +_invalid_version: + controller->SetFailed("invalid version information"); + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h index 557a30f..15c2f5f 100644 --- a/server/myservice.h +++ b/server/myservice.h @@ -97,6 +97,10 @@ public: const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); + virtual void checkVersion(::google::protobuf::RpcController* controller, + const ::OstProto::VersionInfo* request, + ::OstProto::VersionCompatibility* response, + ::google::protobuf::Closure* done); private: /* diff --git a/test/rpctest.py b/test/rpctest.py index cbb3f13..5b341cb 100644 --- a/test/rpctest.py +++ b/test/rpctest.py @@ -63,6 +63,7 @@ class TestSuite: host_name = '127.0.0.1' tx_port_number = -1 rx_port_number = -1 +drone_version = ['0', '0', '0'] if sys.platform == 'win32': tshark = r'C:\Program Files\Wireshark\tshark.exe' @@ -91,7 +92,123 @@ drone = DroneProxy(host_name) try: # ----------------------------------------------------------------- # - # Baseline Configuration + # TESTCASE: Verify any RPC before checkVersion() fails and the server + # closes the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('anyRpcBeforeCheckVersionFails') + drone.channel.connect(drone.host, drone.port) + try: + port_id_list = drone.getPortIdList() + except RpcError as e: + if ('compatibility check pending' in str(e)): + passed = True + else: + raise + finally: + drone.channel.disconnect() + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify DroneProxy.connect() fails for incompatible version + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('connectFailsForIncompatibleVersion') + try: + drone.proxy_version = '0.1.1' + drone.connect() + except RpcError as e: + if ('needs client version' in str(e)): + passed = True + drone_version = str(e).split()[-1].split('.') + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() fails for invalid client version format + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionFailsForInvalidClientVersion') + try: + drone.proxy_version = '0-1-1' + drone.connect() + except RpcError as e: + if ('invalid version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns incompatible if the 'major' + # part of the numbering format is + # different than the server's version and the server closes + # the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsIncompatForDifferentMajorVersion') + try: + drone.proxy_version = (str(int(drone_version[0])+1) + + '.' + drone_version[1]) + drone.connect() + except RpcError as e: + #FIXME: How to check for a closed connection? + if ('needs client version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns incompatible if the 'minor' + # part of the numbering format is + # different than the server's version and the server closes + # the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsIncompatForDifferentMinorVersion') + try: + drone.proxy_version = (drone_version[0] + + '.' + str(int(drone_version[1])+1)) + drone.connect() + except RpcError as e: + #FIXME: How to check for a closed connection? + if ('needs client version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns compatible if the 'revision' + # part of the numbering format is + # different than the server's version + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsCompatForDifferentRevisionVersion') + try: + drone.proxy_version = (drone_version[0] + + '.' + drone_version[1] + + '.' + '999') + drone.connect() + passed = True + except RpcError as e: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # Baseline Configuration for subsequent testcases # ----------------------------------------------------------------- # # connect to drone @@ -125,7 +242,7 @@ try: log.warning('loopback port not found') sys.exit(1) - print('Using port %d as tx/rx port(s)') + print('Using port %d as tx/rx port(s)' % tx_port_number) tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; @@ -176,6 +293,31 @@ try: drone.clearStats(tx_port) drone.clearStats(rx_port) + # ----------------------------------------------------------------- # + # TODO: + # TESTCASE: Verify a RPC with missing required fields in request fails + # and subsequently passes when the fields are initialized + # ----------------------------------------------------------------- # +# passed = False +# suite.test_begin('rpcWithMissingRequiredFieldsFails') +# pid = ost_pb.PortId() +# try: +# sid_list = drone.getStreamIdList(pid) +# except RpcError as e: +# if ('missing required fields in request' in str(e)): +# passed = True +# else: +# raise +# +# try: +# pid.id = tx_port_number +# sid_list = drone.getStreamIdList(pid) +# except RpcError as e: +# passed = False +# raise +# finally: +# suite.test_end(passed) + # ----------------------------------------------------------------- # # TESTCASE: Verify invoking addStream() during transmit fails # TESTCASE: Verify invoking modifyStream() during transmit fails From 57be4f3ada2925b46759d5430078cfb2c2971b6e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 11:26:26 +0530 Subject: [PATCH 272/294] Drone now disconnects only if version is incompatible or if any other RPC is received before versionCheck() - for all other errors/failures, drone does not disconnect the connection with client --- rpc/rpcconn.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index 3d91c75..f315610 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -219,6 +219,7 @@ void RpcConnection::on_clientSock_dataAvail() ::google::protobuf::Message *req, *resp; PbRpcController *controller; QString error; + bool disconnect = false; // Do we have enough bytes for a msg header? // If yes, peek into the header and get msg length @@ -260,6 +261,7 @@ void RpcConnection::on_clientSock_dataAvail() qDebug("server(%s): version compatibility check pending", __FUNCTION__); error = "version compatibility check pending"; + disconnect = true; goto _error_exit; } @@ -347,7 +349,8 @@ _error_exit2: isPending = true; controller = new PbRpcController(NULL, NULL); controller->SetFailed(error); - controller->TriggerDisconnect(); + if (disconnect) + controller->TriggerDisconnect(); sendRpcReply(controller); return; } From 347fc02dcf12b730fc27e6482ece7b4772594274 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 13:02:59 +0530 Subject: [PATCH 273/294] Drone prints version/revision information at startup --- server/drone_main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 7d8453b..726ecc4 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -30,6 +30,8 @@ along with this program. If not, see #endif extern ProtocolManager *OstProtocolManager; +extern char *version; +extern char *revision; int myport; @@ -60,6 +62,9 @@ int main(int argc, char *argv[]) goto _exit; } + qDebug("Version: %s", version); + qDebug("Revision: %s", revision); + #ifdef Q_OS_UNIX struct sigaction sa; memset(&sa, 0, sizeof(sa)); From 470d390658d1ce56fb43b7c2aa55c8cbb0d3e25e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 18:36:01 +0530 Subject: [PATCH 274/294] fix gcc warnings --- client/port.cpp | 2 +- client/streamconfigdialog.cpp | 1 + common/streambase.cpp | 2 +- rpc/pbrpcchannel.cpp | 4 +++- rpc/pbrpccontroller.h | 2 +- rpc/rpcconn.cpp | 3 ++- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/client/port.cpp b/client/port.cpp index 7f1b4c9..4e0f49b 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -121,7 +121,7 @@ void Port::recalculateAverageRates() void Port::setAveragePacketRate(double packetsPerSec) { - double rate; + double rate = 0; double pps = 0; double bps = 0; int n = 0; diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 8752f21..90ba959 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -811,6 +811,7 @@ void StreamConfigDialog::__updateProtocol(int level, int newId) ret = skipProtocols(level-1); Q_ASSERT(ret == true); + Q_UNUSED(ret); Q_ASSERT(oldId != newId); Q_ASSERT(newId != ButtonIdOther); diff --git a/common/streambase.cpp b/common/streambase.cpp index ab43117..be9ce53 100644 --- a/common/streambase.cpp +++ b/common/streambase.cpp @@ -363,7 +363,7 @@ bool StreamBase::setBurstRate(double burstsPerSec) /*! Convenience Function */ double StreamBase::averagePacketRate() const { - double avgPacketRate; + double avgPacketRate = 0; switch (sendUnit()) { diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp index f1fec25..516a023 100644 --- a/rpc/pbrpcchannel.cpp +++ b/rpc/pbrpcchannel.cpp @@ -96,7 +96,7 @@ void PbRpcChannel::CallMethod( ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { - char* const msg = (char*) &msgBuf[0]; + char* msg = (char*) &msgBuf[0]; int len; bool ret; @@ -157,6 +157,7 @@ void PbRpcChannel::CallMethod( mpSocket->write(msg, PB_HDR_SIZE); ret = req->SerializeToZeroCopyStream(outStream); Q_ASSERT(ret == true); + Q_UNUSED(ret); outStream->Flush(); } @@ -182,6 +183,7 @@ void PbRpcChannel::on_mpSocket_readyRead() msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); Q_ASSERT(msgLen == PB_HDR_SIZE); + Q_UNUSED(msgLen); type = qFromBigEndian(msg+0); method = qFromBigEndian(msg+2); diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h index eff2324..788fc2b 100644 --- a/rpc/pbrpccontroller.h +++ b/rpc/pbrpccontroller.h @@ -56,7 +56,7 @@ public: // Server Side Methods void SetFailed(const QString &reason) - { failed = true; errStr = reason; qWarning(qPrintable(errStr)); } + { failed = true; errStr = reason; qWarning("%s", qPrintable(errStr)); } void SetFailed(const std::string &reason) { SetFailed(QString::fromStdString(reason)); } QString ErrorString() const { return errStr; } diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp index f315610..f55a224 100644 --- a/rpc/rpcconn.cpp +++ b/rpc/rpcconn.cpp @@ -149,6 +149,7 @@ void RpcConnection::sendRpcReply(PbRpcController *controller) len = blob->read(msg, sizeof(msgBuf)); l = clientSock->write(msg, len); Q_ASSERT(l == len); + Q_UNUSED(l); } goto _exit; @@ -355,7 +356,7 @@ _error_exit2: return; } -void RpcConnection::connIdMsgHandler(QtMsgType type, const char* msg) +void RpcConnection::connIdMsgHandler(QtMsgType /*type*/, const char* msg) { if (connId.hasLocalData()) { QString newMsg(*connId.localData()); From 63ea66fa4ffb2ae3403c7269fa22e26abe49f3df Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 18:59:59 +0530 Subject: [PATCH 275/294] Fix sys.path in rpctest.py to prepend instead of append the binding directory --- test/rpctest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rpctest.py b/test/rpctest.py index 5b341cb..3ec8ec7 100644 --- a/test/rpctest.py +++ b/test/rpctest.py @@ -7,7 +7,7 @@ import subprocess import sys import time -sys.path.append('../binding') +sys.path.insert(1, '../binding') from core import ost_pb, DroneProxy from rpc import RpcError from protocols.mac_pb2 import mac From 09a79a010db45953b579bf806225b6466713b7d3 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 6 Jul 2014 19:28:30 +0530 Subject: [PATCH 276/294] Fixing compiler warning --- client/port.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/port.cpp b/client/port.cpp index 4e0f49b..e9e5915 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -192,7 +192,7 @@ void Port::setAveragePacketRate(double packetsPerSec) void Port::setAverageBitRate(double bitsPerSec) { - double rate; + double rate = 0; double pps = 0; double bps = 0; int n = 0; From ad11bf8cbd3a14f91438f25e38a5cfb02de1d449 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 7 Jul 2014 17:57:22 +0530 Subject: [PATCH 277/294] Bumping version to 0.6 --- version.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.pri b/version.pri index a76c889..97b3673 100644 --- a/version.pri +++ b/version.pri @@ -1,4 +1,4 @@ -APP_VERSION = 0.5.1 +APP_VERSION = 0.6 APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ From 48215ae93a2cc9b9d2ce28ec2d2a878ce82c347a Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 3 Nov 2014 20:49:42 +0530 Subject: [PATCH 278/294] Make Items in "All Protocols" and "Selected Protocols List" read-only --- client/streamconfigdialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 90ba959..2458372 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -136,8 +136,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager->protocolDatabase(), this); lvAllProtocols->setModel(mpAvailableProtocolsModel); + lvAllProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); mpSelectedProtocolsModel = new QStringListModel(this); lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + lvSelectedProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(lvAllProtocols->selectionModel(), From 943eb49b668bc07c121453cc6a645827ebe05f5d Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Mon, 3 Nov 2014 21:22:45 +0530 Subject: [PATCH 279/294] Fix scrolling issue in packet view protocol/field tree Fixes issue 140 --- client/streamconfigdialog.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui index 4b3a066..e109c79 100644 --- a/client/streamconfigdialog.ui +++ b/client/streamconfigdialog.ui @@ -1138,6 +1138,9 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff QAbstractItemView::SelectItems + + QAbstractItemView::ScrollPerPixel + true From 50f20a545209edddd6e0743e6dbef922727b26b5 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 7 Nov 2014 20:21:02 +0530 Subject: [PATCH 280/294] During stream configuration - when deleting a protocol, delete its associated widget also, otherwise if a newly allocated protocol gets the same heap address as the old protocol, the old protocol's widget gets associated with the new protocol. Fixes issue 131 --- client/streamconfigdialog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp index 2458372..32a4ca7 100644 --- a/client/streamconfigdialog.cpp +++ b/client/streamconfigdialog.cpp @@ -516,6 +516,8 @@ void StreamConfigDialog::on_tbDelete_clicked() Q_CHECK_PTR(p); _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); delete p; updateSelectProtocolsAdvancedWidget(); @@ -835,6 +837,8 @@ void StreamConfigDialog::__updateProtocol(int level, int newId) newId, mpStream)); else _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); delete p; if (level == ProtoPayload) { @@ -842,6 +846,8 @@ void StreamConfigDialog::__updateProtocol(int level, int newId) { p = _iter->next(); _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); delete p; } } From 42f716e693283e876e61ee97bc34e49c59bf314e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sat, 8 Nov 2014 10:17:57 +0530 Subject: [PATCH 281/294] When reading PDML files, skip all XML tokens at the start of the file till you reach the first element and then check whether it is 'pdml' Fixes issue 132 --- common/pdmlfileformat.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp index 9ba1f2d..e567d99 100644 --- a/common/pdmlfileformat.cpp +++ b/common/pdmlfileformat.cpp @@ -141,7 +141,14 @@ bool PdmlFileFormat::isMyFileFormat(const QString fileName) if (xml.hasError() || !xml.isStartDocument()) goto _close_exit; - xml.readNext(); + // skip everything until the start of the first element + while (!xml.isStartElement()) + { + xml.readNext(); + if (xml.hasError()) + goto _close_exit; + } + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) ret = true; else From 1bc4efe48b789a0183569ed67b8500517f5fb434 Mon Sep 17 00:00:00 2001 From: John Paul Spiro-Colwell Date: Sat, 27 Dec 2014 12:16:42 +0530 Subject: [PATCH 282/294] Correct names of ports in Port Stats Window. The problem is seen only with 2 (or more) portgroups where the number of ports in the subsequent portgroup is more than the number of ports in the previous portgroup Fixes issue 88 --- client/portstatsmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp index cd53d4d..1658980 100644 --- a/client/portstatsmodel.cpp +++ b/client/portstatsmodel.cpp @@ -77,7 +77,7 @@ void PortStatsModel::getDomainIndexes(const QModelIndex &index, if (portGroupIdx) { if (numPorts.at(portGroupIdx -1)) - portIdx = (portNum - 1) % numPorts.at(portGroupIdx - 1); + portIdx = (portNum - 1) - numPorts.at(portGroupIdx - 1); else portIdx = portNum - 1; } From 8369c18b3538a39d4f024a3e2395420d84044f16 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 31 Dec 2014 20:16:30 +0530 Subject: [PATCH 283/294] Implemented drone application settings - .ini format on all platforms; settings read at startup, not saved back. In other words, user needs to hand write settings in the .ini file. Currently, the only setting implemented is a portlist filter as include/exclude lists of patterns --- server/drone_main.cpp | 30 +++++++++++++++++++--- server/portmanager.cpp | 56 +++++++++++++++++++++++++++++++++++++++--- server/portmanager.h | 3 +++ server/settings.h | 34 +++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 server/settings.h diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 726ecc4..01bfea6 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -20,10 +20,12 @@ along with this program. If not, see #include "drone.h" #include "../common/protocolmanager.h" +#include "settings.h" #include #include +#include #ifdef Q_OS_UNIX #include @@ -33,6 +35,7 @@ extern ProtocolManager *OstProtocolManager; extern char *version; extern char *revision; +QSettings *appSettings; int myport; void cleanup(int /*signum*/) @@ -44,10 +47,7 @@ int main(int argc, char *argv[]) { int exitCode = 0; QCoreApplication app(argc, argv); - Drone *drone = new Drone(); - OstProtocolManager = new ProtocolManager(); - - app.setApplicationName(drone->objectName()); + Drone *drone; // TODO: command line options // -v (--version) @@ -56,6 +56,28 @@ int main(int argc, char *argv[]) if (argc > 1) myport = atoi(argv[1]); + app.setApplicationName("Drone"); + app.setOrganizationName("Ostinato"); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/drone.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(QSettings::IniFormat, + QSettings::UserScope, + app.organizationName(), + app.applicationName().toLower()); + + if (QFile::exists(appSettings->fileName())) + qDebug("Read settings from %s", qPrintable(appSettings->fileName())); + + drone = new Drone(); + OstProtocolManager = new ProtocolManager(); + if (!drone->init()) { exitCode = -1; diff --git a/server/portmanager.cpp b/server/portmanager.cpp index 2981464..897ad4f 100644 --- a/server/portmanager.cpp +++ b/server/portmanager.cpp @@ -19,14 +19,15 @@ along with this program. If not, see #include "portmanager.h" -#include -#include - #include "bsdport.h" #include "linuxport.h" #include "pcapport.h" +#include "settings.h" #include "winpcapport.h" +#include +#include + PortManager *PortManager::instance_ = NULL; PortManager::PortManager() @@ -49,6 +50,18 @@ PortManager::PortManager() if (device->description) qDebug(" (%s)\n", device->description); +#if defined(Q_OS_WIN32) + if (!filterAcceptsPort(device->description)) +#else + if (!filterAcceptsPort(device->name)) +#endif + { + qDebug("%s (%s) rejected by filter. Skipping!", + device->name, device->description); + i--; + continue; + } + #if defined(Q_OS_WIN32) port = new WinPcapPort(i, device->name); #elif defined(Q_OS_LINUX) @@ -92,3 +105,40 @@ PortManager* PortManager::instance() return instance_; } + +bool PortManager::filterAcceptsPort(const char *name) +{ + QRegExp pattern; + QStringList includeList = appSettings->value(kPortListIncludeKey) + .toStringList(); + QStringList excludeList = appSettings->value(kPortListExcludeKey) + .toStringList(); + + pattern.setPatternSyntax(QRegExp::Wildcard); + + // An empty (or missing) includeList accepts all ports + // NOTE: A blank "IncludeList=" is read as a stringlist with one + // string which is empty => treat it same as an empty stringlist + if (includeList.isEmpty() + || (includeList.size() == 1 && includeList.at(0).isEmpty())) + goto _include_pass; + + foreach (QString str, includeList) { + pattern.setPattern(str); + if (pattern.exactMatch(name)) + goto _include_pass; + } + + // IncludeList is not empty and port did not match a pattern + return false; + +_include_pass: + foreach (QString str, excludeList) { + pattern.setPattern(str); + if (pattern.exactMatch(name)) + return false; + } + + // Port did not match a pattern in ExcludeList + return true; +} diff --git a/server/portmanager.h b/server/portmanager.h index 2407bf2..801fbf1 100644 --- a/server/portmanager.h +++ b/server/portmanager.h @@ -34,6 +34,9 @@ public: static PortManager* instance(); +private: + bool filterAcceptsPort(const char *name); + private: QList portList_; diff --git a/server/settings.h b/server/settings.h new file mode 100644 index 0000000..50a3b03 --- /dev/null +++ b/server/settings.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +// +// PortList Section Keys +// +const QString kPortListIncludeKey("PortList/Include"); +const QString kPortListExcludeKey("PortList/Exclude"); + +#endif From 5d7e6fe66c43845134ad82051873c8a5dae76099 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 31 Dec 2014 21:41:12 +0530 Subject: [PATCH 284/294] Removed debug print of drone settings filename - QSettings::fileName() is the file where the settings will be written to, not the file from which settings were read (when fallbacks are enabled) and hence not useful for us. --- server/drone_main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/drone_main.cpp b/server/drone_main.cpp index 01bfea6..e5c1abd 100644 --- a/server/drone_main.cpp +++ b/server/drone_main.cpp @@ -72,9 +72,6 @@ int main(int argc, char *argv[]) app.organizationName(), app.applicationName().toLower()); - if (QFile::exists(appSettings->fileName())) - qDebug("Read settings from %s", qPrintable(appSettings->fileName())); - drone = new Drone(); OstProtocolManager = new ProtocolManager(); From 37711fdd5c352c3f3e25b9b961912d4caeb7274b Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 1 Jan 2015 21:00:23 +0530 Subject: [PATCH 285/294] Fixed typo that was causing "Clear All Stats" to clear only the first portgroup and not the rest Fixes issue 89 --- client/portstatswindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 035a23c..49f8141 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -148,7 +148,7 @@ void PortStatsWindow::on_tbClearAll_clicked() { for (int i = 0; i < pgl->numPortGroups(); i++) { - pgl->portGroupByIndex(0).clearPortStats(); + pgl->portGroupByIndex(i).clearPortStats(); } } From e2a44314188e4e0e39f6b6f2f7e59a054d3657c7 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 9 Jan 2015 20:48:58 +0530 Subject: [PATCH 286/294] Invoking Port Filter Dialog will now always show selected list in the same order as in the port stats window before invocation --- client/portstatsfilterdialog.cpp | 9 ++++++--- client/portstatsfilterdialog.h | 3 ++- client/portstatswindow.cpp | 26 ++++++++++++++++---------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp index 55882f1..c2aec10 100644 --- a/client/portstatsfilterdialog.cpp +++ b/client/portstatsfilterdialog.cpp @@ -24,7 +24,8 @@ PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) { setupUi(this); - mUnselected.setSortRole(PositionRole); + mUnselected.setSortRole(kLogicalIndex); + mSelected.setSortRole(kVisualIndex); lvUnselected->setModel(&mUnselected); lvSelected->setModel(&mSelected); @@ -49,7 +50,8 @@ QList PortStatsFilterDialog::getItemList(bool* ok, QStandardItem *item; item = new QStandardItem(model->headerData(i, orientation).toString()); - item->setData(i, PositionRole); + item->setData(i, kLogicalIndex); + item->setData(initial.indexOf(i), kVisualIndex); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled //| Qt::ItemIsDropEnabled @@ -60,6 +62,7 @@ QList PortStatsFilterDialog::getItemList(bool* ok, else mUnselected.appendRow(item); } + mSelected.sort(0); // No need to sort right now 'coz we have inserted items in order @@ -70,7 +73,7 @@ QList PortStatsFilterDialog::getItemList(bool* ok, { QModelIndex index = mSelected.index(i, 0, QModelIndex()); QStandardItem *item = mSelected.itemFromIndex(index); - ret.append(item->data(PositionRole).toInt()); + ret.append(item->data(kLogicalIndex).toInt()); } *ok = true; } diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h index 7eea255..97a0d37 100644 --- a/client/portstatsfilterdialog.h +++ b/client/portstatsfilterdialog.h @@ -38,7 +38,8 @@ public: private: enum ItemRole { - PositionRole = Qt::UserRole + 1 + kLogicalIndex = Qt::UserRole + 1, + kVisualIndex }; QStandardItemModel mUnselected; QStandardItemModel mSelected; diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp index 49f8141..d82a1fa 100644 --- a/client/portstatswindow.cpp +++ b/client/portstatswindow.cpp @@ -158,23 +158,29 @@ void PortStatsWindow::on_tbFilter_clicked() QList currentColumns, newColumns; PortStatsFilterDialog dialog; - for(int i = 0; i < model->columnCount(); i++) - if (!tvPortStats->isColumnHidden(i)) - currentColumns.append(i); + // create the input list for the filter dialog - + // list of logical-indexes ordered by their current visual indexes + for(int vi = 0; vi < model->columnCount(); vi++) { + int li = tvPortStats->horizontalHeader()->logicalIndex(vi); + if (!tvPortStats->isColumnHidden(li)) { + currentColumns.append(li); + } + } + // return list from the filter dialog - + // list of logical-indexes ordered by their new visual indexes newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); if (ok) { + QHeaderView *hv = tvPortStats->horizontalHeader(); + // hide/show sections first ... - for(int i = 0; i < model->columnCount(); i++) - tvPortStats->setColumnHidden(i, !newColumns.contains(i)); + for(int li = 0; li < model->columnCount(); li++) + tvPortStats->setColumnHidden(li, !newColumns.contains(li)); // ... then for the 'shown' columns, set the visual index - for(int i = 0; i < newColumns.size(); i++) - { - tvPortStats->horizontalHeader()->moveSection(tvPortStats-> - horizontalHeader()->visualIndex(newColumns.at(i)), i); - } + for(int vi = 0; vi < newColumns.size(); vi++) + hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi); } } From f4f5214b7a1e13fae6903f5db59ecbe8d9dcda4c Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Tue, 13 Jan 2015 18:51:21 +0530 Subject: [PATCH 287/294] If no L4 protocol follows IPv6, set the IPv6 Next-Header field to 0x3B (IPv6-No-Next-Header) instead of leaving it at 0 which is interpreted as IPv6-Hop-By-Hop by the recipient and subsequent failure in parsing the frame --- common/ip6.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/ip6.cpp b/common/ip6.cpp index 79a0868..8833516 100644 --- a/common/ip6.cpp +++ b/common/ip6.cpp @@ -262,10 +262,17 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib, case FieldValue: case FieldFrameValue: case FieldTextValue: - if (data.is_override_next_header()) + if (data.is_override_next_header()) { nextHdr = data.next_header(); - else + } + 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 From 02e442a6bf7e2a3fc392dc361114a5336d1c863e Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 15 Jan 2015 20:22:24 +0530 Subject: [PATCH 288/294] Check for updates (newer version) at startup - for now only Ostinato does this check, not drone. --- client/mainwindow.cpp | 11 ++++ client/mainwindow.h | 3 + client/ostinato.pro | 6 +- client/updater.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++ client/updater.h | 52 +++++++++++++++++ 5 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 client/updater.cpp create mode 100644 client/updater.h diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 79cd708..f69a856 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -29,6 +29,7 @@ along with this program. If not, see #include "preferences.h" #include "settings.h" #include "ui_about.h" +#include "updater.h" #include #include @@ -42,6 +43,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) { QString serverApp = QCoreApplication::applicationDirPath(); + Updater *updater = new Updater(); #ifdef Q_OS_MAC // applicationDirPath() does not return bundle, but executable inside bundle @@ -90,6 +92,10 @@ MainWindow::MainWindow(QWidget *parent) connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + + connect(updater, SIGNAL(newVersionAvailable(QString)), + this, SLOT(onNewVersion(QString))); + updater->checkForNewVersion(); #if 0 { DbgThread *dbg = new DbgThread(pgl); @@ -140,3 +146,8 @@ void MainWindow::on_actionHelpAbout_triggered() delete aboutDialog; } +void MainWindow::onNewVersion(QString newVersion) +{ + statusBar()->showMessage(QString("New Ostinato version %1 available. " + "Visit http://ostinato.org to download").arg(newVersion)); +} diff --git a/client/mainwindow.h b/client/mainwindow.h index 2f2602d..2b68ca5 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -47,6 +47,9 @@ public: public slots: void on_actionPreferences_triggered(); void on_actionHelpAbout_triggered(); + +private slots: + void onNewVersion(QString version); }; #endif diff --git a/client/ostinato.pro b/client/ostinato.pro index 9a4af25..456f349 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -50,7 +50,8 @@ HEADERS += \ settings.h \ streamconfigdialog.h \ streamlistdelegate.h \ - streammodel.h + streammodel.h \ + updater.h FORMS += \ about.ui \ @@ -81,7 +82,8 @@ SOURCES += \ preferences.cpp \ streamconfigdialog.cpp \ streamlistdelegate.cpp \ - streammodel.cpp + streammodel.cpp \ + updater.cpp QMAKE_DISTCLEAN += object_script.* diff --git a/client/updater.cpp b/client/updater.cpp new file mode 100644 index 0000000..669cfb2 --- /dev/null +++ b/client/updater.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2015 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 "updater.h" + +#include +#include +#include + +extern const char* version; + +Updater::Updater() +{ + http_ = NULL; + file_ = NULL; + +#if 1 + // Tests! + Q_ASSERT(isVersionNewer("1.1", "1") == true); + Q_ASSERT(isVersionNewer("10.1", "2") == true); + Q_ASSERT(isVersionNewer("0.10", "0.2") == true); + Q_ASSERT(isVersionNewer("1.10.1", "1.2.3") == true); +#endif +} + +Updater::~Updater() +{ + delete http_; + delete file_; +} + +void Updater::checkForNewVersion() +{ + http_ = new QHttp("wiki.ostinato.googlecode.com"); + file_ = new QTemporaryFile(); + + connect(http_, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), + this, SLOT(responseReceived(QHttpResponseHeader))); + connect(http_, SIGNAL(requestFinished(int, bool)), + this, SLOT(parseXml(int, bool))); + connect(http_, SIGNAL(stateChanged(int)), + this, SLOT(stateUpdate(int))); + + file_->open(); + qDebug("Updater: PAD XML file - %s", qPrintable(file_->fileName())); + + http_->get("/hg/html/pad.xml", file_); + qDebug("Updater: %s", qPrintable(http_->currentRequest().toString() + .replace("\r\n", "\nUpdater: "))); +} + +void Updater::stateUpdate(int state) +{ + qDebug("Updater: state %d", state); +} + +void Updater::responseReceived(QHttpResponseHeader response) +{ + qDebug("Updater: HTTP/%d.%d %d %s", + response.majorVersion(), response.minorVersion(), + response.statusCode(), qPrintable(response.reasonPhrase())); +} + +void Updater::parseXml(int id, bool error) +{ + QXmlStreamReader xml; + QString newVersion; + + if (error) { + qDebug("Updater: %s", qPrintable(http_->errorString())); + goto _exit; + } + + // Close and reopen the file so that we read from the top + file_->close(); + file_->open(); + xml.setDevice(file_); + + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement() && (xml.name() == "Program_Version")) + newVersion = xml.readElementText(); + } + + qDebug("Updater: latest version = %s", qPrintable(newVersion)); + if (!newVersion.isEmpty() && isVersionNewer(newVersion, QString(version))) + emit newVersionAvailable(newVersion); + +_exit: + // Job done, time to self-destruct + deleteLater(); +} + +bool Updater::isVersionNewer(QString newVersion, QString curVersion) +{ + QStringList curVer = QString(curVersion).split('.'); + QStringList newVer = QString(newVersion).split('.'); + + for (int i = 0; i < qMin(curVer.size(), newVer.size()); i++) { + bool isOk; + if (newVer.at(i).toUInt(&isOk) > curVer.at(i).toUInt(&isOk)) + return true; + } + + if (newVer.size() > curVer.size()) + return true; + + return false; +} + + diff --git a/client/updater.h b/client/updater.h new file mode 100644 index 0000000..2d61e3d --- /dev/null +++ b/client/updater.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2015 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 +*/ + +#ifndef _UPDATER_H +#define _UPDATER_H + +#include +#include + +class QHttp; +class QTemporaryFile; + +class Updater : public QObject +{ + Q_OBJECT +public: + Updater(); + virtual ~Updater(); + void checkForNewVersion(); + static bool isVersionNewer(QString newVersion, QString curVersion); + +signals: + void newVersionAvailable(QString); + +private slots: + void stateUpdate(int state); + void responseReceived(QHttpResponseHeader response); + void parseXml(int id, bool error); + +private: + QHttp *http_; + QTemporaryFile *file_; +}; + +#endif + From ac7e378939c560b861794bfaaae4d0a6e10a3843 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 16 Jan 2015 20:29:11 +0530 Subject: [PATCH 289/294] Names of capture files on the client now include the interface name so that it is easy to identify which capture belongs to which port when multiple captures are open in multiple Wireshark windows. --- client/port.h | 6 +++++- server/pcapport.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/port.h b/client/port.h index 10c2803..6df7a98 100644 --- a/client/port.h +++ b/client/port.h @@ -20,6 +20,7 @@ along with this program. If not, see #ifndef _PORT_H #define _PORT_H +#include #include #include #include @@ -101,7 +102,10 @@ public: QTemporaryFile* getCaptureFile() { delete capFile_; - capFile_ = new QTemporaryFile(); + capFile_ = new QTemporaryFile(QString(QDir::tempPath()) + .append("/") + .append(name()) + .append(".XXXXXX")); return capFile_; } diff --git a/server/pcapport.cpp b/server/pcapport.cpp index 8b500f2..c46d5ad 100644 --- a/server/pcapport.cpp +++ b/server/pcapport.cpp @@ -98,7 +98,7 @@ PcapPort::PcapPort(int id, const char *device) if (strcmp(device, dev->name) == 0) { #ifdef Q_OS_WIN32 - data_.set_name(QString("if%1 ").arg(id).toStdString()); + data_.set_name(QString("if%1").arg(id).toStdString()); #else if (dev->name) data_.set_name(dev->name); From b617c44e61f348d4c4ff25f06b027d8de866d44f Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Wed, 18 Feb 2015 21:20:27 +0530 Subject: [PATCH 290/294] 64bit stats support added for Linux if supported by the kernel and driver Fixes issue 70 --- server/drone.pro | 2 + server/linuxport.cpp | 107 +++++++++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/server/drone.pro b/server/drone.pro index 54efd8b..d22f138 100644 --- a/server/drone.pro +++ b/server/drone.pro @@ -3,6 +3,8 @@ CONFIG += qt ver_info QT += network script QT -= gui DEFINES += HAVE_REMOTE WPCAP +linux*:system(grep -q IFLA_STATS64 /usr/include/linux/if_link.h): \ + DEFINES += HAVE_IFLA_STATS64 INCLUDEPATH += "../rpc" win32 { CONFIG += console diff --git a/server/linuxport.cpp b/server/linuxport.cpp index 18cf525..55468ef 100644 --- a/server/linuxport.cpp +++ b/server/linuxport.cpp @@ -40,6 +40,15 @@ QList LinuxPort::allPorts_; LinuxPort::StatsMonitor *LinuxPort::monitor_; const quint32 kMaxValue32 = 0xffffffff; +const quint64 kMaxValue64 = 0xffffffffffffffffULL; + +#ifdef HAVE_IFLA_STATS64 +#define X_IFLA_STATS IFLA_STATS64 +typedef struct rtnl_link_stats64 x_rtnl_link_stats; +#else +#define X_IFLA_STATS IFLA_STATS +typedef struct rtnl_link_stats x_rtnl_link_stats; +#endif LinuxPort::LinuxPort(int id, const char *device) : PcapPort(id, device) @@ -62,7 +71,10 @@ LinuxPort::LinuxPort(int id, const char *device) qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); - maxStatsValue_ = 0xffffffff; + // A port can support either 32 or 64 bit stats - we will attempt + // to guess this for each port and initialize this variable at + // run time when the counter wraps around + maxStatsValue_ = 0; } LinuxPort::~LinuxPort() @@ -332,6 +344,7 @@ void LinuxPort::StatsMonitor::procStats() AbstractPort::PortStats *stats = portStats[index]; if (stats) { + // TODO: fix the pps/Bps calc similar to netlink stats stats->rxPps = ((rxPkts >= stats->rxPkts) ? rxPkts - stats->rxPkts : @@ -378,6 +391,7 @@ void LinuxPort::StatsMonitor::procStats() int LinuxPort::StatsMonitor::netlinkStats() { QHash portStats; + QHash portMaxStatsValue; QHash linkState; int fd; struct sockaddr_nl local; @@ -555,6 +569,8 @@ _retry: if (strcmp(port->name(), ifname) == 0) { portStats[uint(ifi->ifi_index)] = &(port->stats_); + portMaxStatsValue[uint(ifi->ifi_index)] = + &(port->maxStatsValue_); linkState[uint(ifi->ifi_index)] = &(port->linkState_); if (setPromisc(port->name())) @@ -640,43 +656,76 @@ _retry_recv: rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); while (RTA_OK(rta, rtaLen)) { - // TODO: IFLA_STATS64 - if (rta->rta_type == IFLA_STATS) + if (rta->rta_type == X_IFLA_STATS) { - struct rtnl_link_stats *rtnlStats = - (struct rtnl_link_stats*) RTA_DATA(rta); + x_rtnl_link_stats *rtnlStats = + (x_rtnl_link_stats*) RTA_DATA(rta); AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + quint64 *maxStatsValue = portMaxStatsValue[ifi->ifi_index]; OstProto::LinkState *state = linkState[ifi->ifi_index]; if (!stats) break; - stats->rxPps = - ((rtnlStats->rx_packets >= stats->rxPkts) ? - rtnlStats->rx_packets - stats->rxPkts : - rtnlStats->rx_packets + (kMaxValue32 - - stats->rxPkts)) - / kRefreshFreq_; - stats->rxBps = - ((rtnlStats->rx_bytes >= stats->rxBytes) ? - rtnlStats->rx_bytes - stats->rxBytes : - rtnlStats->rx_bytes + (kMaxValue32 - - stats->rxBytes)) - / kRefreshFreq_; + if (rtnlStats->rx_packets >= stats->rxPkts) { + stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->rxPkts > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->rxPps = ((*maxStatsValue - stats->rxPkts) + + rtnlStats->rx_packets) + / kRefreshFreq_; + } + + if (rtnlStats->rx_bytes >= stats->rxBytes) { + stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->rxBytes > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->rxBps = ((*maxStatsValue - stats->rxBytes) + + rtnlStats->rx_bytes) + / kRefreshFreq_; + } + stats->rxPkts = rtnlStats->rx_packets; stats->rxBytes = rtnlStats->rx_bytes; - stats->txPps = - ((rtnlStats->tx_packets >= stats->txPkts) ? - rtnlStats->tx_packets - stats->txPkts : - rtnlStats->tx_packets + (kMaxValue32 - - stats->txPkts)) - / kRefreshFreq_; - stats->txBps = - ((rtnlStats->tx_bytes >= stats->txBytes) ? - rtnlStats->tx_bytes - stats->txBytes : - rtnlStats->tx_bytes + (kMaxValue32 - - stats->txBytes)) - / kRefreshFreq_; + + if (rtnlStats->tx_packets >= stats->txPkts) { + stats->txPps = (rtnlStats->tx_packets - stats->txPkts) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->txPkts > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->txPps = ((*maxStatsValue - stats->txPkts) + + rtnlStats->tx_packets) + / kRefreshFreq_; + } + + if (rtnlStats->tx_bytes >= stats->txBytes) { + stats->txBps = (rtnlStats->tx_bytes - stats->txBytes) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->txBytes > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->txBps = ((*maxStatsValue - stats->txBytes) + + rtnlStats->tx_bytes) + / kRefreshFreq_; + } + stats->txPkts = rtnlStats->tx_packets; stats->txBytes = rtnlStats->tx_bytes; From c0f6a7112391f029098da22ca55385c87e476646 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Fri, 27 Feb 2015 20:29:38 +0530 Subject: [PATCH 291/294] Fix infinite loop(s) when QT_NO_DEBUG_OUTPUT is defined Fixes issue 146 --- .hgignore | 38 + .vimrc | 5 + COPYING | 674 +++++++++++ binding/LICENSE.txt | 674 +++++++++++ binding/README.txt | 9 + binding/__init__.py | 28 + binding/binding.pro | 4 + binding/core.py | 67 ++ binding/example.py | 188 ++++ binding/protocols/__init__.py | 17 + binding/rpc.py | 167 +++ binding/setup.py | 84 ++ client/about.ui | 211 ++++ client/dumpview.cpp | 408 +++++++ client/dumpview.h | 61 + client/hexlineedit.cpp | 91 ++ client/hexlineedit.h | 43 + client/icons/about.png | Bin 0 -> 1036 bytes client/icons/arrow_down.png | Bin 0 -> 379 bytes client/icons/arrow_left.png | Bin 0 -> 345 bytes client/icons/arrow_right.png | Bin 0 -> 349 bytes client/icons/arrow_up.png | Bin 0 -> 372 bytes client/icons/bullet_error.png | Bin 0 -> 454 bytes client/icons/bullet_green.png | Bin 0 -> 295 bytes client/icons/bullet_orange.png | Bin 0 -> 283 bytes client/icons/bullet_red.png | Bin 0 -> 287 bytes client/icons/bullet_white.png | Bin 0 -> 201 bytes client/icons/bullet_yellow.png | Bin 0 -> 287 bytes client/icons/control_play.png | Bin 0 -> 592 bytes client/icons/control_stop.png | Bin 0 -> 403 bytes client/icons/deco_exclusive.png | Bin 0 -> 793 bytes client/icons/delete.png | Bin 0 -> 715 bytes client/icons/exit.png | Bin 0 -> 688 bytes client/icons/gaps.png | Bin 0 -> 3467 bytes client/icons/logo.icns | Bin 0 -> 35391 bytes client/icons/logo.ico | Bin 0 -> 2238 bytes client/icons/logo.png | Bin 0 -> 18467 bytes client/icons/magnifier.png | Bin 0 -> 615 bytes client/icons/name.png | Bin 0 -> 2813 bytes client/icons/portgroup_add.png | Bin 0 -> 781 bytes client/icons/portgroup_connect.png | Bin 0 -> 748 bytes client/icons/portgroup_delete.png | Bin 0 -> 775 bytes client/icons/portgroup_disconnect.png | Bin 0 -> 796 bytes client/icons/portstats_clear.png | Bin 0 -> 367 bytes client/icons/portstats_clear_all.png | Bin 0 -> 736 bytes client/icons/portstats_filter.png | Bin 0 -> 432 bytes client/icons/preferences.png | Bin 0 -> 584 bytes client/icons/qt.png | Bin 0 -> 2037 bytes client/icons/sound_mute.png | Bin 0 -> 474 bytes client/icons/sound_none.png | Bin 0 -> 417 bytes client/icons/stream_add.png | Bin 0 -> 814 bytes client/icons/stream_delete.png | Bin 0 -> 847 bytes client/icons/stream_edit.png | Bin 0 -> 865 bytes client/main.cpp | 91 ++ client/mainwindow.cpp | 153 +++ client/mainwindow.h | 56 + client/mainwindow.ui | 84 ++ client/modeltest.cpp | 542 +++++++++ client/modeltest.h | 76 ++ client/modeltest.pri | 4 + client/ostinato.pro | 95 ++ client/ostinato.qrc | 38 + client/ostinato.rc | 1 + client/packetmodel.cpp | 244 ++++ client/packetmodel.h | 61 + client/port.cpp | 573 ++++++++++ client/port.h | 151 +++ client/portconfigdialog.cpp | 57 + client/portconfigdialog.h | 39 + client/portconfigdialog.ui | 109 ++ client/portgroup.cpp | 896 +++++++++++++++ client/portgroup.h | 157 +++ client/portgrouplist.cpp | 133 +++ client/portgrouplist.h | 75 ++ client/portmodel.cpp | 339 ++++++ client/portmodel.h | 75 ++ client/portstatsfilter.ui | 170 +++ client/portstatsfilterdialog.cpp | 134 +++ client/portstatsfilterdialog.h | 55 + client/portstatsmodel.cpp | 326 ++++++ client/portstatsmodel.h | 151 +++ client/portstatswindow.cpp | 186 ++++ client/portstatswindow.h | 56 + client/portstatswindow.ui | 182 +++ client/portswindow.cpp | 663 +++++++++++ client/portswindow.h | 86 ++ client/portswindow.ui | 299 +++++ client/preferences.cpp | 119 ++ client/preferences.h | 45 + client/preferences.ui | 226 ++++ client/settings.h | 86 ++ client/stream.cpp | 51 + client/stream.h | 42 + client/streamconfigdialog.cpp | 1212 ++++++++++++++++++++ client/streamconfigdialog.h | 150 +++ client/streamconfigdialog.ui | 1481 +++++++++++++++++++++++++ client/streamlistdelegate.cpp | 194 ++++ client/streamlistdelegate.h | 48 + client/streammodel.cpp | 288 +++++ client/streammodel.h | 78 ++ client/updater.cpp | 127 +++ client/updater.h | 52 + common/abstractfileformat.cpp | 127 +++ common/abstractfileformat.h | 91 ++ common/abstractprotocol.cpp | 898 +++++++++++++++ common/abstractprotocol.h | 160 +++ common/abstractprotocolconfig.h | 120 ++ common/arp.cpp | 825 ++++++++++++++ common/arp.h | 103 ++ common/arp.proto | 67 ++ common/arp.ui | 518 +++++++++ common/arpconfig.cpp | 270 +++++ common/arpconfig.h | 46 + common/arppdml.cpp | 41 + common/arppdml.h | 34 + common/comboprotocol.h | 190 ++++ common/comboprotocolconfig.h | 100 ++ common/crc32c.cpp | 134 +++ common/crc32c.h | 23 + common/dot2llc.h | 30 + common/dot2llc.proto | 33 + common/dot2llcconfig.h | 36 + common/dot2snap.h | 30 + common/dot2snap.proto | 31 + common/dot2snapconfig.h | 36 + common/dot3.cpp | 196 ++++ common/dot3.h | 67 ++ common/dot3.proto | 32 + common/dot3.ui | 86 ++ common/dot3config.cpp | 63 ++ common/dot3config.h | 41 + common/eth2.cpp | 188 ++++ common/eth2.h | 66 ++ common/eth2.proto | 33 + common/eth2.ui | 80 ++ common/eth2config.cpp | 63 ++ common/eth2config.h | 41 + common/eth2pdml.cpp | 124 +++ common/eth2pdml.h | 38 + common/fileformat.cpp | 483 ++++++++ common/fileformat.h | 62 ++ common/fileformat.proto | 98 ++ common/gmp.cpp | 769 +++++++++++++ common/gmp.h | 129 +++ common/gmp.proto | 94 ++ common/gmp.ui | 835 ++++++++++++++ common/gmpconfig.cpp | 384 +++++++ common/gmpconfig.h | 63 ++ common/hexdump.cpp | 211 ++++ common/hexdump.h | 73 ++ common/hexdump.proto | 32 + common/hexdump.ui | 76 ++ common/hexdumpconfig.cpp | 75 ++ common/hexdumpconfig.h | 44 + common/icmp.cpp | 396 +++++++ common/icmp.h | 96 ++ common/icmp.proto | 44 + common/icmp.ui | 178 +++ common/icmp6pdml.cpp | 100 ++ common/icmp6pdml.h | 50 + common/icmpconfig.cpp | 191 ++++ common/icmpconfig.h | 50 + common/icmphelper.h | 88 ++ common/icmppdml.cpp | 93 ++ common/icmppdml.h | 48 + common/igmp.cpp | 364 ++++++ common/igmp.h | 98 ++ common/igmp.proto | 27 + common/igmpconfig.cpp | 112 ++ common/igmpconfig.h | 40 + common/igmppdml.cpp | 141 +++ common/igmppdml.h | 49 + common/intcombobox.h | 69 ++ common/ip4.cpp | 846 ++++++++++++++ common/ip4.h | 99 ++ common/ip4.proto | 66 ++ common/ip4.ui | 516 +++++++++ common/ip4config.cpp | 297 +++++ common/ip4config.h | 44 + common/ip4over4.h | 91 ++ common/ip4over4.proto | 37 + common/ip4over4config.h | 35 + common/ip4over6.h | 30 + common/ip4over6.proto | 31 + common/ip4over6config.h | 35 + common/ip4pdml.cpp | 93 ++ common/ip4pdml.h | 41 + common/ip6.cpp | 813 ++++++++++++++ common/ip6.h | 112 ++ common/ip6.proto | 61 + common/ip6.ui | 467 ++++++++ common/ip6config.cpp | 233 ++++ common/ip6config.h | 44 + common/ip6over4.h | 30 + common/ip6over4.proto | 31 + common/ip6over4config.h | 35 + common/ip6over6.h | 91 ++ common/ip6over6.proto | 37 + common/ip6over6config.h | 33 + common/ip6pdml.cpp | 76 ++ common/ip6pdml.h | 39 + common/iputils.h | 122 ++ common/ipv4addressdelegate.h | 58 + common/ipv6addressdelegate.h | 60 + common/ipv6addressvalidator.h | 75 ++ common/llc.cpp | 264 +++++ common/llc.h | 71 ++ common/llc.proto | 36 + common/llc.ui | 161 +++ common/llcconfig.cpp | 100 ++ common/llcconfig.h | 41 + common/llcpdml.cpp | 80 ++ common/llcpdml.h | 39 + common/mac.cpp | 351 ++++++ common/mac.h | 73 ++ common/mac.proto | 48 + common/mac.ui | 188 ++++ common/macconfig.cpp | 148 +++ common/macconfig.h | 45 + common/mld.cpp | 543 +++++++++ common/mld.h | 95 ++ common/mld.proto | 27 + common/mldconfig.cpp | 109 ++ common/mldconfig.h | 41 + common/mldpdml.cpp | 133 +++ common/mldpdml.h | 47 + common/ostproto.pro | 114 ++ common/ostprotogui.pro | 129 +++ common/ostprotolib.cpp | 55 + common/ostprotolib.h | 42 + common/payload.cpp | 258 +++++ common/payload.h | 71 ++ common/payload.proto | 41 + common/payload.ui | 106 ++ common/payloadconfig.cpp | 84 ++ common/payloadconfig.h | 44 + common/pcapfileformat.cpp | 661 +++++++++++ common/pcapfileformat.h | 87 ++ common/pcapfileimport.ui | 132 +++ common/pdmlfileformat.cpp | 170 +++ common/pdmlfileformat.h | 42 + common/pdmlprotocol.cpp | 248 +++++ common/pdmlprotocol.h | 68 ++ common/pdmlprotocols.cpp | 195 ++++ common/pdmlprotocols.h | 71 ++ common/pdmlreader.cpp | 548 +++++++++ common/pdmlreader.h | 75 ++ common/protocol.proto | 265 +++++ common/protocollist.cpp | 27 + common/protocollist.h | 28 + common/protocollistiterator.cpp | 133 +++ common/protocollistiterator.h | 48 + common/protocolmanager.cpp | 232 ++++ common/protocolmanager.h | 58 + common/protocolwidgetfactory.cpp | 193 ++++ common/protocolwidgetfactory.h | 46 + common/sample.cpp | 477 ++++++++ common/sample.h | 89 ++ common/sample.proto | 38 + common/sample.ui | 191 ++++ common/sampleconfig.cpp | 117 ++ common/sampleconfig.h | 43 + common/samplepdml.cpp | 118 ++ common/samplepdml.h | 50 + common/snap.cpp | 255 +++++ common/snap.h | 70 ++ common/snap.proto | 34 + common/snap.ui | 122 ++ common/snapconfig.cpp | 78 ++ common/snapconfig.h | 41 + common/streambase.cpp | 567 ++++++++++ common/streambase.h | 150 +++ common/svlan.cpp | 66 ++ common/svlan.h | 42 + common/svlan.proto | 27 + common/svlanconfig.h | 28 + common/svlanpdml.cpp | 110 ++ common/svlanpdml.h | 40 + common/tcp.cpp | 606 ++++++++++ common/tcp.h | 89 ++ common/tcp.proto | 47 + common/tcp.ui | 268 +++++ common/tcpconfig.cpp | 175 +++ common/tcpconfig.h | 41 + common/tcppdml.cpp | 98 ++ common/tcppdml.h | 42 + common/textproto.cpp | 240 ++++ common/textproto.h | 77 ++ common/textproto.proto | 44 + common/textproto.ui | 108 ++ common/textprotoconfig.cpp | 84 ++ common/textprotoconfig.h | 41 + common/textprotopdml.cpp | 171 +++ common/textprotopdml.h | 53 + common/udp.cpp | 431 +++++++ common/udp.h | 75 ++ common/udp.proto | 39 + common/udp.ui | 174 +++ common/udpconfig.cpp | 114 ++ common/udpconfig.h | 41 + common/udppdml.cpp | 53 + common/udppdml.h | 35 + common/userscript.cpp | 555 +++++++++ common/userscript.h | 162 +++ common/userscript.proto | 33 + common/userscript.ui | 70 ++ common/userscriptconfig.cpp | 109 ++ common/userscriptconfig.h | 51 + common/vlan.cpp | 270 +++++ common/vlan.h | 67 ++ common/vlan.proto | 34 + common/vlan.ui | 181 +++ common/vlanconfig.cpp | 87 ++ common/vlanconfig.h | 41 + common/vlanpdml.cpp | 91 ++ common/vlanpdml.h | 40 + common/vlanstack.h | 30 + common/vlanstack.proto | 31 + common/vlanstackconfig.h | 38 + extra/extra.pro | 3 + extra/qhexedit2/qhexedit2.pro | 12 + extra/qhexedit2/src/commands.cpp | 115 ++ extra/qhexedit2/src/commands.h | 70 ++ extra/qhexedit2/src/license.txt | 502 +++++++++ extra/qhexedit2/src/qhexedit.cpp | 152 +++ extra/qhexedit2/src/qhexedit.h | 205 ++++ extra/qhexedit2/src/qhexedit_p.cpp | 800 +++++++++++++ extra/qhexedit2/src/qhexedit_p.h | 120 ++ extra/qhexedit2/src/xbytearray.cpp | 167 +++ extra/qhexedit2/src/xbytearray.h | 66 ++ install.pri | 14 + ost.pro | 11 + protobuf.pri | 42 + rpc/pbhelper.h | 170 +++ rpc/pbqtio.h | 42 + rpc/pbrpc.pro | 7 + rpc/pbrpcchannel.cpp | 390 +++++++ rpc/pbrpcchannel.h | 106 ++ rpc/pbrpccommon.h | 40 + rpc/pbrpccontroller.h | 88 ++ rpc/rpcconn.cpp | 372 +++++++ rpc/rpcconn.h | 75 ++ rpc/rpcserver.cpp | 80 ++ rpc/rpcserver.h | 50 + server/abstractport.cpp | 594 ++++++++++ server/abstractport.h | 127 +++ server/bsdport.cpp | 355 ++++++ server/bsdport.h | 61 + server/drone.cpp | 53 + server/drone.h | 40 + server/drone.pro | 49 + server/drone_main.cpp | 107 ++ server/icons/portgroup.png | Bin 0 -> 667 bytes server/linuxport.cpp | 819 ++++++++++++++ server/linuxport.h | 68 ++ server/myservice.cpp | 581 ++++++++++ server/myservice.h | 121 ++ server/pcapextra.cpp | 78 ++ server/pcapextra.h | 45 + server/pcapport.cpp | 857 ++++++++++++++ server/pcapport.h | 236 ++++ server/portmanager.cpp | 144 +++ server/portmanager.h | 46 + server/settings.h | 34 + server/winpcapport.cpp | 226 ++++ server/winpcapport.h | 56 + test/main.cpp | 109 ++ test/rpctest.py | 554 +++++++++ test/test.pro | 39 + version.pri | 42 + 370 files changed, 52566 insertions(+) create mode 100644 .hgignore create mode 100644 .vimrc create mode 100644 COPYING create mode 100644 binding/LICENSE.txt create mode 100644 binding/README.txt create mode 100644 binding/__init__.py create mode 100644 binding/binding.pro create mode 100644 binding/core.py create mode 100644 binding/example.py create mode 100644 binding/protocols/__init__.py create mode 100644 binding/rpc.py create mode 100644 binding/setup.py create mode 100644 client/about.ui create mode 100644 client/dumpview.cpp create mode 100644 client/dumpview.h create mode 100644 client/hexlineedit.cpp create mode 100644 client/hexlineedit.h create mode 100644 client/icons/about.png create mode 100644 client/icons/arrow_down.png create mode 100644 client/icons/arrow_left.png create mode 100644 client/icons/arrow_right.png create mode 100644 client/icons/arrow_up.png create mode 100644 client/icons/bullet_error.png create mode 100644 client/icons/bullet_green.png create mode 100644 client/icons/bullet_orange.png create mode 100644 client/icons/bullet_red.png create mode 100644 client/icons/bullet_white.png create mode 100644 client/icons/bullet_yellow.png create mode 100644 client/icons/control_play.png create mode 100644 client/icons/control_stop.png create mode 100644 client/icons/deco_exclusive.png create mode 100644 client/icons/delete.png create mode 100644 client/icons/exit.png create mode 100644 client/icons/gaps.png create mode 100644 client/icons/logo.icns create mode 100644 client/icons/logo.ico create mode 100644 client/icons/logo.png create mode 100644 client/icons/magnifier.png create mode 100644 client/icons/name.png create mode 100644 client/icons/portgroup_add.png create mode 100644 client/icons/portgroup_connect.png create mode 100644 client/icons/portgroup_delete.png create mode 100644 client/icons/portgroup_disconnect.png create mode 100644 client/icons/portstats_clear.png create mode 100644 client/icons/portstats_clear_all.png create mode 100644 client/icons/portstats_filter.png create mode 100644 client/icons/preferences.png create mode 100644 client/icons/qt.png create mode 100644 client/icons/sound_mute.png create mode 100644 client/icons/sound_none.png create mode 100644 client/icons/stream_add.png create mode 100644 client/icons/stream_delete.png create mode 100644 client/icons/stream_edit.png create mode 100644 client/main.cpp create mode 100644 client/mainwindow.cpp create mode 100644 client/mainwindow.h create mode 100644 client/mainwindow.ui create mode 100644 client/modeltest.cpp create mode 100644 client/modeltest.h create mode 100644 client/modeltest.pri create mode 100644 client/ostinato.pro create mode 100644 client/ostinato.qrc create mode 100644 client/ostinato.rc create mode 100644 client/packetmodel.cpp create mode 100644 client/packetmodel.h create mode 100644 client/port.cpp create mode 100644 client/port.h create mode 100644 client/portconfigdialog.cpp create mode 100644 client/portconfigdialog.h create mode 100644 client/portconfigdialog.ui create mode 100644 client/portgroup.cpp create mode 100644 client/portgroup.h create mode 100644 client/portgrouplist.cpp create mode 100644 client/portgrouplist.h create mode 100644 client/portmodel.cpp create mode 100644 client/portmodel.h create mode 100644 client/portstatsfilter.ui create mode 100644 client/portstatsfilterdialog.cpp create mode 100644 client/portstatsfilterdialog.h create mode 100644 client/portstatsmodel.cpp create mode 100644 client/portstatsmodel.h create mode 100644 client/portstatswindow.cpp create mode 100644 client/portstatswindow.h create mode 100644 client/portstatswindow.ui create mode 100644 client/portswindow.cpp create mode 100644 client/portswindow.h create mode 100644 client/portswindow.ui create mode 100644 client/preferences.cpp create mode 100644 client/preferences.h create mode 100644 client/preferences.ui create mode 100644 client/settings.h create mode 100644 client/stream.cpp create mode 100644 client/stream.h create mode 100644 client/streamconfigdialog.cpp create mode 100644 client/streamconfigdialog.h create mode 100644 client/streamconfigdialog.ui create mode 100644 client/streamlistdelegate.cpp create mode 100644 client/streamlistdelegate.h create mode 100644 client/streammodel.cpp create mode 100644 client/streammodel.h create mode 100644 client/updater.cpp create mode 100644 client/updater.h create mode 100644 common/abstractfileformat.cpp create mode 100644 common/abstractfileformat.h create mode 100644 common/abstractprotocol.cpp create mode 100644 common/abstractprotocol.h create mode 100644 common/abstractprotocolconfig.h create mode 100644 common/arp.cpp create mode 100644 common/arp.h create mode 100644 common/arp.proto create mode 100644 common/arp.ui create mode 100644 common/arpconfig.cpp create mode 100644 common/arpconfig.h create mode 100644 common/arppdml.cpp create mode 100644 common/arppdml.h create mode 100644 common/comboprotocol.h create mode 100644 common/comboprotocolconfig.h create mode 100644 common/crc32c.cpp create mode 100644 common/crc32c.h create mode 100644 common/dot2llc.h create mode 100644 common/dot2llc.proto create mode 100644 common/dot2llcconfig.h create mode 100644 common/dot2snap.h create mode 100644 common/dot2snap.proto create mode 100644 common/dot2snapconfig.h create mode 100644 common/dot3.cpp create mode 100644 common/dot3.h create mode 100644 common/dot3.proto create mode 100644 common/dot3.ui create mode 100644 common/dot3config.cpp create mode 100644 common/dot3config.h create mode 100644 common/eth2.cpp create mode 100644 common/eth2.h create mode 100644 common/eth2.proto create mode 100644 common/eth2.ui create mode 100644 common/eth2config.cpp create mode 100644 common/eth2config.h create mode 100644 common/eth2pdml.cpp create mode 100644 common/eth2pdml.h create mode 100644 common/fileformat.cpp create mode 100644 common/fileformat.h create mode 100644 common/fileformat.proto create mode 100755 common/gmp.cpp create mode 100755 common/gmp.h create mode 100755 common/gmp.proto create mode 100755 common/gmp.ui create mode 100644 common/gmpconfig.cpp create mode 100644 common/gmpconfig.h create mode 100644 common/hexdump.cpp create mode 100644 common/hexdump.h create mode 100644 common/hexdump.proto create mode 100644 common/hexdump.ui create mode 100644 common/hexdumpconfig.cpp create mode 100644 common/hexdumpconfig.h create mode 100644 common/icmp.cpp create mode 100644 common/icmp.h create mode 100644 common/icmp.proto create mode 100644 common/icmp.ui create mode 100644 common/icmp6pdml.cpp create mode 100644 common/icmp6pdml.h create mode 100644 common/icmpconfig.cpp create mode 100644 common/icmpconfig.h create mode 100644 common/icmphelper.h create mode 100644 common/icmppdml.cpp create mode 100644 common/icmppdml.h create mode 100644 common/igmp.cpp create mode 100644 common/igmp.h create mode 100755 common/igmp.proto create mode 100644 common/igmpconfig.cpp create mode 100644 common/igmpconfig.h create mode 100644 common/igmppdml.cpp create mode 100644 common/igmppdml.h create mode 100644 common/intcombobox.h create mode 100644 common/ip4.cpp create mode 100644 common/ip4.h create mode 100644 common/ip4.proto create mode 100644 common/ip4.ui create mode 100644 common/ip4config.cpp create mode 100644 common/ip4config.h create mode 100644 common/ip4over4.h create mode 100644 common/ip4over4.proto create mode 100644 common/ip4over4config.h create mode 100644 common/ip4over6.h create mode 100644 common/ip4over6.proto create mode 100644 common/ip4over6config.h create mode 100644 common/ip4pdml.cpp create mode 100644 common/ip4pdml.h create mode 100644 common/ip6.cpp create mode 100644 common/ip6.h create mode 100644 common/ip6.proto create mode 100644 common/ip6.ui create mode 100644 common/ip6config.cpp create mode 100644 common/ip6config.h create mode 100644 common/ip6over4.h create mode 100644 common/ip6over4.proto create mode 100644 common/ip6over4config.h create mode 100644 common/ip6over6.h create mode 100644 common/ip6over6.proto create mode 100644 common/ip6over6config.h create mode 100644 common/ip6pdml.cpp create mode 100644 common/ip6pdml.h create mode 100644 common/iputils.h create mode 100644 common/ipv4addressdelegate.h create mode 100644 common/ipv6addressdelegate.h create mode 100644 common/ipv6addressvalidator.h create mode 100644 common/llc.cpp create mode 100644 common/llc.h create mode 100644 common/llc.proto create mode 100644 common/llc.ui create mode 100644 common/llcconfig.cpp create mode 100644 common/llcconfig.h create mode 100644 common/llcpdml.cpp create mode 100644 common/llcpdml.h create mode 100644 common/mac.cpp create mode 100644 common/mac.h create mode 100644 common/mac.proto create mode 100644 common/mac.ui create mode 100644 common/macconfig.cpp create mode 100644 common/macconfig.h create mode 100644 common/mld.cpp create mode 100644 common/mld.h create mode 100755 common/mld.proto create mode 100644 common/mldconfig.cpp create mode 100644 common/mldconfig.h create mode 100644 common/mldpdml.cpp create mode 100644 common/mldpdml.h create mode 100644 common/ostproto.pro create mode 100644 common/ostprotogui.pro create mode 100644 common/ostprotolib.cpp create mode 100644 common/ostprotolib.h create mode 100644 common/payload.cpp create mode 100644 common/payload.h create mode 100644 common/payload.proto create mode 100644 common/payload.ui create mode 100644 common/payloadconfig.cpp create mode 100644 common/payloadconfig.h create mode 100644 common/pcapfileformat.cpp create mode 100644 common/pcapfileformat.h create mode 100644 common/pcapfileimport.ui create mode 100644 common/pdmlfileformat.cpp create mode 100644 common/pdmlfileformat.h create mode 100644 common/pdmlprotocol.cpp create mode 100644 common/pdmlprotocol.h create mode 100644 common/pdmlprotocols.cpp create mode 100644 common/pdmlprotocols.h create mode 100644 common/pdmlreader.cpp create mode 100644 common/pdmlreader.h create mode 100644 common/protocol.proto create mode 100644 common/protocollist.cpp create mode 100644 common/protocollist.h create mode 100644 common/protocollistiterator.cpp create mode 100644 common/protocollistiterator.h create mode 100644 common/protocolmanager.cpp create mode 100644 common/protocolmanager.h create mode 100644 common/protocolwidgetfactory.cpp create mode 100644 common/protocolwidgetfactory.h create mode 100644 common/sample.cpp create mode 100644 common/sample.h create mode 100644 common/sample.proto create mode 100644 common/sample.ui create mode 100644 common/sampleconfig.cpp create mode 100644 common/sampleconfig.h create mode 100644 common/samplepdml.cpp create mode 100644 common/samplepdml.h create mode 100644 common/snap.cpp create mode 100644 common/snap.h create mode 100644 common/snap.proto create mode 100644 common/snap.ui create mode 100644 common/snapconfig.cpp create mode 100644 common/snapconfig.h create mode 100644 common/streambase.cpp create mode 100644 common/streambase.h create mode 100644 common/svlan.cpp create mode 100644 common/svlan.h create mode 100644 common/svlan.proto create mode 100644 common/svlanconfig.h create mode 100644 common/svlanpdml.cpp create mode 100644 common/svlanpdml.h create mode 100644 common/tcp.cpp create mode 100644 common/tcp.h create mode 100644 common/tcp.proto create mode 100644 common/tcp.ui create mode 100644 common/tcpconfig.cpp create mode 100644 common/tcpconfig.h create mode 100644 common/tcppdml.cpp create mode 100644 common/tcppdml.h create mode 100644 common/textproto.cpp create mode 100644 common/textproto.h create mode 100644 common/textproto.proto create mode 100644 common/textproto.ui create mode 100644 common/textprotoconfig.cpp create mode 100644 common/textprotoconfig.h create mode 100644 common/textprotopdml.cpp create mode 100644 common/textprotopdml.h create mode 100644 common/udp.cpp create mode 100644 common/udp.h create mode 100644 common/udp.proto create mode 100644 common/udp.ui create mode 100644 common/udpconfig.cpp create mode 100644 common/udpconfig.h create mode 100644 common/udppdml.cpp create mode 100644 common/udppdml.h create mode 100644 common/userscript.cpp create mode 100644 common/userscript.h create mode 100644 common/userscript.proto create mode 100644 common/userscript.ui create mode 100644 common/userscriptconfig.cpp create mode 100644 common/userscriptconfig.h create mode 100644 common/vlan.cpp create mode 100644 common/vlan.h create mode 100644 common/vlan.proto create mode 100644 common/vlan.ui create mode 100644 common/vlanconfig.cpp create mode 100644 common/vlanconfig.h create mode 100644 common/vlanpdml.cpp create mode 100644 common/vlanpdml.h create mode 100644 common/vlanstack.h create mode 100644 common/vlanstack.proto create mode 100644 common/vlanstackconfig.h create mode 100644 extra/extra.pro create mode 100644 extra/qhexedit2/qhexedit2.pro create mode 100644 extra/qhexedit2/src/commands.cpp create mode 100644 extra/qhexedit2/src/commands.h create mode 100644 extra/qhexedit2/src/license.txt create mode 100644 extra/qhexedit2/src/qhexedit.cpp create mode 100644 extra/qhexedit2/src/qhexedit.h create mode 100644 extra/qhexedit2/src/qhexedit_p.cpp create mode 100644 extra/qhexedit2/src/qhexedit_p.h create mode 100644 extra/qhexedit2/src/xbytearray.cpp create mode 100644 extra/qhexedit2/src/xbytearray.h create mode 100644 install.pri create mode 100644 ost.pro create mode 100644 protobuf.pri create mode 100644 rpc/pbhelper.h create mode 100644 rpc/pbqtio.h create mode 100644 rpc/pbrpc.pro create mode 100644 rpc/pbrpcchannel.cpp create mode 100644 rpc/pbrpcchannel.h create mode 100644 rpc/pbrpccommon.h create mode 100644 rpc/pbrpccontroller.h create mode 100644 rpc/rpcconn.cpp create mode 100644 rpc/rpcconn.h create mode 100644 rpc/rpcserver.cpp create mode 100644 rpc/rpcserver.h create mode 100644 server/abstractport.cpp create mode 100644 server/abstractport.h create mode 100644 server/bsdport.cpp create mode 100644 server/bsdport.h create mode 100644 server/drone.cpp create mode 100644 server/drone.h create mode 100644 server/drone.pro create mode 100644 server/drone_main.cpp create mode 100644 server/icons/portgroup.png create mode 100644 server/linuxport.cpp create mode 100644 server/linuxport.h create mode 100644 server/myservice.cpp create mode 100644 server/myservice.h create mode 100644 server/pcapextra.cpp create mode 100644 server/pcapextra.h create mode 100644 server/pcapport.cpp create mode 100644 server/pcapport.h create mode 100644 server/portmanager.cpp create mode 100644 server/portmanager.h create mode 100644 server/settings.h create mode 100644 server/winpcapport.cpp create mode 100644 server/winpcapport.h create mode 100644 test/main.cpp create mode 100644 test/rpctest.py create mode 100644 test/test.pro create mode 100644 version.pri diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..48fc886 --- /dev/null +++ b/.hgignore @@ -0,0 +1,38 @@ +syntax: glob + +# generated object files +*.pyc +*.o +*.a +*.dll +*.so +*.exe +*.app +drone +ostinato + +# Qt generated files +ui_*.h +moc_*.cpp +qrc_*.cpp + +# QMake generated files +Makefile* +*\object_script.* + +# protobuf generated files +*.pb.h +*.pb.cc +*_pb2.py + +# ostinato generated files +version.cpp +pkg_info.json + +# vim swap files +*.swp +.DS_Store + +# ctags +tags + diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..fd28004 --- /dev/null +++ b/.vimrc @@ -0,0 +1,5 @@ +set shiftwidth=4 +set tabstop=8 +set softtabstop=4 +set expandtab +set cindent diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/binding/LICENSE.txt b/binding/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/binding/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/binding/README.txt b/binding/README.txt new file mode 100644 index 0000000..b0de5ec --- /dev/null +++ b/binding/README.txt @@ -0,0 +1,9 @@ +=============== +python-ostinato +=============== + +python-ostinato provides python bindings for Ostinato. + +Ostinato is a network packet/traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. + +Documentation is available in the wiki at http://ostinato.org diff --git a/binding/__init__.py b/binding/__init__.py new file mode 100644 index 0000000..8c8376d --- /dev/null +++ b/binding/__init__.py @@ -0,0 +1,28 @@ +# Copyright (C) 2014 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 + +import json +import logging +from os.path import dirname + +with open(dirname(__file__) + '/pkg_info.json') as f: + _info = json.load(f) + +__version__ = _info['version'] +__revision__ = _info['revision'] + +__log__ = logging.getLogger(__name__) diff --git a/binding/binding.pro b/binding/binding.pro new file mode 100644 index 0000000..68b7a58 --- /dev/null +++ b/binding/binding.pro @@ -0,0 +1,4 @@ +TEMPLATE = lib +CONFIG += pkg_info + +include(../version.pri) diff --git a/binding/core.py b/binding/core.py new file mode 100644 index 0000000..d8de580 --- /dev/null +++ b/binding/core.py @@ -0,0 +1,67 @@ +# Copyright (C) 2014 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 + +import os +from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError +import protocols.protocol_pb2 as ost_pb +from __init__ import __version__ + +class DroneProxy(object): + + def __init__(self, host_name, port_number=7878): + self.host = host_name + self.port = port_number + self.channel = OstinatoRpcChannel() + self.stub = ost_pb.OstService_Stub(self.channel) + self.void = ost_pb.Void() + + for method in self.stub.GetDescriptor().methods: + fn = lambda request=self.void, method_name=method.name: \ + self.callRpcMethod(method_name, request) + self.__dict__[method.name] = fn + + def hostName(self): + return self.host + + def portNumber(self): + return self.port + + def connect(self): + self.channel.connect(self.host, self.port) + ver = ost_pb.VersionInfo() + ver.version = __version__ + compat = self.checkVersion(ver) + if compat.result == ost_pb.VersionCompatibility.kIncompatible: + raise RpcError('incompatible version %s (%s)' % + (ver.version, compat.notes)) + + def disconnect(self): + self.channel.disconnect() + + def callRpcMethod(self, method_name, request): + controller = OstinatoRpcController() + ost_pb.OstService_Stub.__dict__[method_name]( + self.stub, controller, request, None) + return controller.response + + def saveCaptureBuffer(self, buffer, file_name): + f= open(file_name, 'wb') + f.write(buffer) + f.flush() + os.fsync(f.fileno()) + f.close() + diff --git a/binding/example.py b/binding/example.py new file mode 100644 index 0000000..0a77a27 --- /dev/null +++ b/binding/example.py @@ -0,0 +1,188 @@ +#! /usr/bin/env python + +# standard modules +import logging +import os +import sys +import time + +# ostinato modules +# (user scripts using the installed package should prepend ostinato. i.e +# ostinato.core and ostinato.protocols) +from core import ost_pb, DroneProxy +from protocols.mac_pb2 import mac +from protocols.ip4_pb2 import ip4, Ip4 + +# initialize defaults +use_defaults = False +host_name = '127.0.0.1' +tx_port_number = 0 +rx_port_number = 0 + +# setup logging +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +# command-line option/arg processing +if len(sys.argv) > 1: + if sys.argv[1] in ('-d', '--use-defaults'): + use_defaults = True + if sys.argv[1] in ('-h', '--help'): + print('%s [OPTION]...' % (sys.argv[0])) + print('Options:') + print(' -d --use-defaults run using default values') + print(' -h --help show this help') + sys.exit(0) + +print('') +print('This example expects the following topology -') +print('') +print(' +-------+ +-------+') +print(' | |Tx--->----| |') +print(' | Drone | | DUT |') +print(' | |Rx---<----| |') +print(' +-------+ +-------+') +print('') +print('Drone has 2 ports connected to DUT. Packets sent on the Tx port') +print('are expected to be received back on the Rx port') +print('') +print('An easy way to simulate the above topology is to select the loopback') +print('port as both Tx and Rx ports') +print('') + +if not use_defaults: + s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) + host_name = s or host_name + +drone = DroneProxy(host_name) + +try: + # connect to drone + log.info('connecting to drone(%s:%d)' + % (drone.hostName(), drone.portNumber())) + drone.connect() + + # retreive port id list + log.info('retreiving port list') + port_id_list = drone.getPortIdList() + + # retreive port config list + log.info('retreiving port config for all ports') + port_config_list = drone.getPortConfig(port_id_list) + + if len(port_config_list.port) == 0: + log.warning('drone has no ports!') + sys.exit(1) + + # print port list and get tx/rx port id + print('Port List') + print('---------') + for port in port_config_list.port: + print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) + # use a loopback port as default tx/rx port + if ('lo' in port.name or 'loopback' in port.description.lower()): + tx_port_number = port.port_id.id + rx_port_number = port.port_id.id + + if not use_defaults: + p = raw_input('Tx Port Id [%d]: ' % (tx_port_number)) + if p: + tx_port_number = int(p) + + p = raw_input('Rx Port Id [%d]: ' % (rx_port_number)) + if p: + rx_port_number = int(p) + + tx_port = ost_pb.PortIdList() + tx_port.port_id.add().id = tx_port_number; + + rx_port = ost_pb.PortIdList() + rx_port.port_id.add().id = rx_port_number; + + # add a stream + stream_id = ost_pb.StreamIdList() + stream_id.port_id.CopyFrom(tx_port.port_id[0]) + stream_id.stream_id.add().id = 1 + log.info('adding tx_stream %d' % stream_id.stream_id[0].id) + drone.addStream(stream_id) + + # configure the stream + stream_cfg = ost_pb.StreamConfigList() + stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) + s = stream_cfg.stream.add() + s.stream_id.id = stream_id.stream_id[0].id + s.core.is_enabled = True + s.control.num_packets = 5 + + # setup stream protocols as mac:eth2:ip4:udp:payload + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber + p.Extensions[mac].dst_mac = 0x001122334455 + p.Extensions[mac].src_mac = 0x00aabbccddee + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber + # reduce typing by creating a shorter reference to p.Extensions[ip4] + ip = p.Extensions[ip4] + ip.src_ip = 0x01020304 + ip.dst_ip = 0x05060708 + ip.dst_ip_mode = Ip4.e_im_inc_host + + s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + + log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) + drone.modifyStream(stream_cfg) + + # clear tx/rx stats + log.info('clearing tx/rx stats') + drone.clearStats(tx_port) + drone.clearStats(rx_port) + + # start capture and transmit + log.info('starting capture') + drone.startCapture(rx_port) + log.info('starting transmit') + drone.startTransmit(tx_port) + + # wait for transmit to finish + log.info('waiting for transmit to finish ...') + time.sleep(7) + + # stop transmit and capture + log.info('stopping transmit') + drone.stopTransmit(tx_port) + log.info('stopping capture') + drone.stopCapture(rx_port) + + # get tx/rx stats + log.info('retreiving stats') + tx_stats = drone.getStats(tx_port) + rx_stats = drone.getStats(rx_port) + + #log.info('--> (tx_stats)' + tx_stats.__str__()) + #log.info('--> (rx_stats)' + rx_stats.__str__()) + log.info('tx pkts = %d, rx pkts = %d' % + (tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts)) + + # retrieve and dump received packets + log.info('getting Rx capture buffer') + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + os.system('tshark -r capture.pcap') + os.remove('capture.pcap') + + # delete streams + log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) + drone.deleteStream(stream_id) + + # bye for now + drone.disconnect() + +except Exception as ex: + log.exception(ex) + sys.exit(1) diff --git a/binding/protocols/__init__.py b/binding/protocols/__init__.py new file mode 100644 index 0000000..161ae68 --- /dev/null +++ b/binding/protocols/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2014 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 + diff --git a/binding/rpc.py b/binding/rpc.py new file mode 100644 index 0000000..2eb721b --- /dev/null +++ b/binding/rpc.py @@ -0,0 +1,167 @@ +# Copyright (C) 2014 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 + +from google.protobuf.message import EncodeError, DecodeError +from google.protobuf.service import RpcChannel +from google.protobuf.service import RpcController +import logging +import socket +import struct +import sys + +class PeerClosedConnError(Exception): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class RpcError(Exception): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class RpcMismatchError(Exception): + def __init__(self, msg, received, expected): + self.msg = msg + self.received = received + self.expected = expected + def __str__(self): + return '%s - Expected method %d, Received method %d' % ( + self.msg, self.expected, self.received) + +class OstinatoRpcController(RpcController): + def __init__(self): + super(OstinatoRpcController, self).__init__() + +class OstinatoRpcChannel(RpcChannel): + def __init__(self): + self.log = logging.getLogger(__name__) + self.log.debug('opening socket') + + def connect(self, host, port): + self.peer = '%s:%d' % (host, port) + self.log.debug('connecting to %s', self.peer) + try: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((host, port)) + except socket.error as e: + error = 'ERROR: Unable to connect to Drone %s (%s)' % ( + self.peer, str(e)) + print(error) + raise + + def disconnect(self): + self.log.debug('closing socket') + self.sock.close() + + def CallMethod(self, method, controller, request, response_class, done): + MSG_HDR_SIZE = 8 + MSG_TYPE_REQUEST = 1 + MSG_TYPE_RESPONSE = 2 + MSG_TYPE_BLOB = 3 + MSG_TYPE_ERROR = 4 + + error = '' + try: + self.log.info('invoking RPC %s(%s): %s', method.name, + type(request).__name__, response_class.__name__) + self.log.debug('serializing request arg %s', request) + req = request.SerializeToString() + hdr = struct.pack('>HHI', MSG_TYPE_REQUEST, method.index, len(req)) + self.log.debug('req.hdr = %r', hdr) + self.sock.sendall(hdr + req) + + # receive and parse header + self.log.debug('receiving response hdr') + hdr = '' + while len(hdr) < MSG_HDR_SIZE: + chunk = self.sock.recv(MSG_HDR_SIZE - len(hdr)) + if chunk == '': + raise PeerClosedConnError('connection closed by peer') + hdr = hdr + chunk + + (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) + self.log.debug('resp hdr: type = %d, method = %d, len = %d', + msg_type, method_index, resp_len) + + # receive and parse the actual response message + self.log.debug('receiving response data') + resp = '' + while len(resp) < resp_len: + chunk = self.sock.recv(resp_len - len(resp)) + if chunk == '': + raise PeerClosedConnError('connection closed by peer') + resp = resp + chunk + + # verify response method is same as the one requested + if method_index != method.index: + raise RpcMismatchError('RPC mismatch', + expected = method.index, received = method_index) + + if msg_type == MSG_TYPE_RESPONSE: + response = response_class() + response.ParseFromString(resp) + self.log.debug('parsed response %s', response) + elif msg_type == MSG_TYPE_BLOB: + response = resp + elif msg_type == MSG_TYPE_ERROR: + raise RpcError(unicode(resp, 'utf-8')) + else: + raise RpcError('unknown RPC msg type %d' % msg_type) + + controller.response = response + + except socket.error as e: + error = 'ERROR: RPC %s() to Drone %s failed (%s)' % ( + method.name, self.peer, e) + self.log.exception(error+e) + raise + except PeerClosedConnError as e: + error = 'ERROR: Drone %s closed connection receiving reply ' \ + 'for RPC %s() (%s)' % ( + self.peer, method.name, e) + self.log.exception(error) + raise + except EncodeError as e: + error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \ + 'to Drone %s (%s)' % ( + type(request).__name__, method.name, self.peer, e) + self.log.exception(error) + raise + except DecodeError as e: + error = 'ERROR: Failed to parse %s response for RPC %s() ' \ + 'from Drone %s (%s)' % ( + type(response).__name__, method.name, self.peer, e) + self.log.exception(error) + raise + except RpcMismatchError as e: + error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % ( + method.name, e) + self.log.exception(error) + raise + except RpcError as e: + error = 'ERROR: error received for RPC %s() (%s) ' % ( + method.name, e) + self.log.exception(error) + raise + finally: + if error: + print(error) + + + diff --git a/binding/setup.py b/binding/setup.py new file mode 100644 index 0000000..8c81b74 --- /dev/null +++ b/binding/setup.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# Copyright (C) 2014 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 + +import json +import os +import shutil +import sys +from setuptools import Command, setup +from setuptools.command.sdist import sdist as _sdist + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +def ensure_cwd(): + if os.path.split(os.getcwd())[1] != 'binding': + print('ERROR: This script needs to be run from the binding directory') + print('Current Working Directory is %s' % os.getcwd()) + sys.exit(1) + +class sdist(_sdist): + def run(self): + ensure_cwd() + _sdist.run(self) + +class sdist_clean(Command): + description = 'clean stuff generated by sdist' + user_options = [] + def initialize_options(self): + return + + def finalize_options(self): + return + + def run(self): + ensure_cwd() + shutil.rmtree('dist', ignore_errors = True) + shutil.rmtree('python-ostinato.egg-info', ignore_errors = True) + shutil.rmtree('python_ostinato.egg-info', ignore_errors = True) + +# ------- script starts from here ------- # + +with open(os.path.join(os.path.dirname(__file__), 'pkg_info.json')) as f: + pkg_info = json.load(f) + +setup(name = 'python-ostinato', + version = pkg_info['version'], + author = 'Srivats P', + author_email = 'pstavirs@gmail.com', + license = "GPLv3+", + url = 'http://ostinato.org', + description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer', + long_description = read('README.txt'), + install_requires = ['protobuf>=2.3.0'], + packages = ['ostinato', 'ostinato.protocols'], + package_dir = {'ostinato': '.'}, + package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']}, + platforms = ['Any'], + classifiers = [ + 'Development Status :: 4 - Beta', + 'Programming Language :: Python :: 2.7', + 'Intended Audience :: Telecommunications Industry', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Topic :: Software Development :: Testing :: Traffic Generation', + 'Topic :: System :: Networking'], + cmdclass={ + 'sdist': sdist, + 'sdist_clean': sdist_clean}, + ) + diff --git a/client/about.ui b/client/about.ui new file mode 100644 index 0000000..93354ea --- /dev/null +++ b/client/about.ui @@ -0,0 +1,211 @@ + + About + + + + 0 + 0 + 500 + 327 + + + + + 0 + 0 + + + + About Ostinato + + + + + + 0 + + + + Ostinato + + + + + + + + + 0 + 0 + + + + + + + :/icons/logo.png + + + false + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + :/icons/name.png + + + Qt::AlignCenter + + + + + + + Version/Revision Placeholder + + + Qt::AlignCenter + + + + + + + Copyright (c) 2007-2014 Srivats P. + + + Qt::AlignCenter + + + + + + + <a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 21 + + + + + + + + + + + + Logo (c): Dhiman Sengupta +Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) + + + Qt::AlignCenter + + + + + + + + License + + + + + + <p>This program 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.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + About + accept() + + + 353 + 280 + + + 286 + 262 + + + + + diff --git a/client/dumpview.cpp b/client/dumpview.cpp new file mode 100644 index 0000000..fe99e01 --- /dev/null +++ b/client/dumpview.cpp @@ -0,0 +1,408 @@ +/* +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 +*/ + +#include "dumpview.h" + +//! \todo Enable Scrollbars + +DumpView::DumpView(QWidget *parent) + : QAbstractItemView(parent) +{ + int w, h; + + // 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 + // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ + 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"); +} + +QModelIndex DumpView::indexAt(const QPoint &/*point*/) const +{ +#if 0 + int x = point.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 = point.y()/mLineHeight; + + if ((col < 16) && (row < ((data.size()+16)/16))) + { + selrow = row; + selcol = 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)", selrow, selcol); + + offset = selrow * 16 + selcol; +#if 0 + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + if (model()->hasChildren(index)) + indexAtOffset(offset, index); // Non Leaf + else + if ( + dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf + // FIXME: Use RawValueRole instead of UserRole + } +#endif +} + +_exit: + // Clear existing selection + selrow = -1; + +#endif + return QModelIndex(); +} + +void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) +{ + // FIXME: implement scrolling +} + +QRect DumpView::visualRect(const QModelIndex &/*index*/) const +{ + // FIXME: calculate actual rect + return rect(); +} + +//protected: +int DumpView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const +{ + return false; +} + +QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, + Qt::KeyboardModifiers /*modifiers*/) +{ + // FIXME(MED): need to implement movement using cursor + return currentIndex(); +} + +void DumpView::setSelection(const QRect &/*rect*/, + QItemSelectionModel::SelectionFlags flags) +{ + // FIXME(HI): calculate indexes using rect + selectionModel()->select(QModelIndex(), flags); +} + +int DumpView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +QRegion DumpView::visualRegionForSelection( + const QItemSelection &/*selection*/) const +{ + // FIXME(HI) + return QRegion(rect()); +} + +//protected slots: +void DumpView::dataChanged(const QModelIndex &/*topLeft*/, + const QModelIndex &/*bottomRight*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + // FIXME(HI) + update(); +} + +void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent) +{ + // FIXME: Use new enum instead of Qt::UserRole + //! \todo (low): generalize this for any model not just our pkt model + + Q_ASSERT(!parent.isValid()); + + qDebug("!!!! %d $$$$", dump.size()); + + for(int i = 0; i < model()->rowCount(parent); i++) + { + QModelIndex index = model()->index(i, 0, parent); + + Q_ASSERT(index.isValid()); + + // Assumption: protocol data is in bytes (not bits) + qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); + dump.append(model()->data(index, Qt::UserRole).toByteArray()); + + } + + if (selectionModel()->selectedIndexes().size()) + { + int j, bits; + QModelIndex index; + + Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); + index = selectionModel()->selectedIndexes().at(0); + + if (index.parent().isValid()) + { + // Field + + // SelOfs = SUM(protocol sizes before selected field's protocol) + + // SUM(field sizes before selected field) + + selOfs = 0; + j = index.parent().row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.parent().sibling(j,0), + Qt::UserRole).toByteArray().size(); + j--; + } + + bits = 0; + j = index.row() - 1; + while (j >= 0) + { + bits += model()->data(index.sibling(j,0), Qt::UserRole+1). + toInt(); + j--; + } + selOfs += bits/8; + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + else + { + // Protocol + selOfs = 0; + j = index.row() - 1; + while (j >= 0) + { + selOfs += model()->data(index.sibling(j,0), Qt::UserRole). + toByteArray().size(); + j--; + } + selSize = model()->data(index, Qt::UserRole).toByteArray().size(); + } + } +} + +// TODO(LOW): rewrite this function - it's a mess! +void DumpView::paintEvent(QPaintEvent* /*event*/) +{ + QStylePainter painter(viewport()); + QRect offsetRect = mOffsetPaneTopRect; + QRect dumpRect = mDumpPaneTopRect; + QRect asciiRect = mAsciiPaneTopRect; + QPalette pal = palette(); + static QByteArray data; + //QByteArray ba; + int selOfs = -1, selSize; + int curSelOfs, curSelSize; + + 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))); + + if (model()) + { + data.clear(); + populateDump(data, selOfs, selSize); + } + + // 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); + + // if no selection, skip selection painting + if (selOfs < 0) + goto _next; + + // Check overlap between current row and selection + { + QRect r1(i, 0, qMin(16, data.size()-i), 8); + QRect s1(selOfs, 0, selSize, 8); + if (r1.intersects(s1)) + { + QRect t = r1.intersected(s1); + + curSelOfs = t.x(); + curSelSize = t.width(); + } + else + curSelSize = 0; + + } + + // overpaint selection on current row (if any) + if (curSelSize > 0) + { + QRect r; + QString selectedAsciiStr, selectedDumpStr; + + qDebug("dumpview::paintEvent - Highlighted (%d, %d)", + curSelOfs, curSelSize); + + // construct the dumpStr and asciiStr + for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) + { + unsigned char c = data.at(k); + + // extra space after 8 bytes + if (((k+8) % 16) == 0) + { + // Avoid adding space at the start for fields starting + // at second column 8 byte boundary + if (k!=curSelOfs) + { + selectedDumpStr.append(" "); + selectedAsciiStr.append(" "); + } + } + + selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, + QChar('0')).toUpper()).append(" "); + + if (isPrintable(c)) + selectedAsciiStr.append(QChar(c)); + else + selectedAsciiStr.append(QChar('.')); + } + + // display dump + r = dumpRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*3, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 3 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 3) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedDumpStr, QPalette::HighlightedText); + + // display ascii + r = asciiRect; + if ((curSelOfs - i) < 8) + r.translate(mCharWidth*(curSelOfs-i)*1, 0); + else + r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); + + // adjust width taking care of selection stretching between + // the two 8byte columns + if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) + r.setWidth((curSelSize * 1 + 1) * mCharWidth); + else + r.setWidth((curSelSize * 1) * mCharWidth); + + painter.fillRect(r, pal.highlight()); + painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, + true, selectedAsciiStr, QPalette::HighlightedText); + } + +_next: + // 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..b170cd0 --- /dev/null +++ b/client/dumpview.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#include // FIXME: High + + +class DumpView: public QAbstractItemView +{ +public: + DumpView(QWidget *parent=0); + + QModelIndex indexAt( const QPoint &point ) const; + void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); + QRect visualRect( const QModelIndex &index ) const; + +protected: + int horizontalOffset() const; + bool isIndexHidden( const QModelIndex &index ) const; + QModelIndex moveCursor( CursorAction cursorAction, + Qt::KeyboardModifiers modifiers ); + void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); + int verticalOffset() const; + QRegion visualRegionForSelection( const QItemSelection &selection ) const; +protected slots: + void dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ); + void selectionChanged( const QItemSelection &selected, + const QItemSelection &deselected ); + void paintEvent(QPaintEvent *event); + +private: + void populateDump(QByteArray &dump, int &selOfs, int &selSize, + QModelIndex parent = QModelIndex()); + bool inline isPrintable(char c) + {if ((c > 48) && (c < 126)) return true; else return false; } + +private: + QRect mOffsetPaneTopRect; + QRect mDumpPaneTopRect; + QRect mAsciiPaneTopRect; + int mSelectedRow, mSelectedCol; + int mLineHeight; + int mCharWidth; +}; + diff --git a/client/hexlineedit.cpp b/client/hexlineedit.cpp new file mode 100644 index 0000000..6150084 --- /dev/null +++ b/client/hexlineedit.cpp @@ -0,0 +1,91 @@ +/* +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 +*/ + +#include "hexlineedit.h" +#include "qdebug.h" + +QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); + +HexLineEdit::HexLineEdit( QWidget * parent) + : QLineEdit(parent) +{ + //QLineEdit::QLineEdit(parent); +} + +void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) +{ +#if 0 + const QValidator *v = validator(); + if ( v ) + { + int curpos = cursorPosition(); + QString str = text(); + if ( v->validate( str, curpos ) == QValidator::Acceptable ) + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + if ( str != text() ) + setText( str ); + } + else + { + if ( curpos != cursorPosition() ) + setCursorPosition( curpos ); + str = text(); + v->fixup( str ); + if ( str != text() ) + { + setText( str ); + } + } + } + QLineEdit::focusOutEvent( e ); + emit focusOut(); +#else +#define uintToHexStr(num, bytesize) \ + QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) + + bool isOk; + ulong num; + + qDebug("before = %s\n", text().toAscii().data()); + num = text().remove(QChar(' ')).toULong(&isOk, 16); + setText(uintToHexStr(num, 4)); + qDebug("after = %s\n", text().toAscii().data()); +#undef uintToHexStr +#endif +} + +#if 0 +void HexLineEdit::focusInEvent( QFocusEvent *e ) +{ + QLineEdit::focusInEvent( e ); + emit focusIn(); +} + +void HexLineEdit::keyPressEvent( QKeyEvent *e ) +{ + QLineEdit::keyPressEvent( e ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) + { + setSelection( 0, text().length() ); + } +} +#endif + diff --git a/client/hexlineedit.h b/client/hexlineedit.h new file mode 100644 index 0000000..20ad460 --- /dev/null +++ b/client/hexlineedit.h @@ -0,0 +1,43 @@ +/* +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 +*/ + +#ifndef _HEXLINEEDIT +#define _HEXLINEEDIT + +#include + +class HexLineEdit : public QLineEdit +{ + Q_OBJECT +public: + // Constructors + HexLineEdit ( QWidget * parent); + +protected: + void focusOutEvent( QFocusEvent *e ); + //void focusInEvent( QFocusEvent *e ); + //void keyPressEvent( QKeyEvent *e ); + +signals: + //void focusIn(); + void focusOut(); +}; + +#endif + diff --git a/client/icons/about.png b/client/icons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..95fb35e1202dcf7f5c61ede0162a23f03f1eee66 GIT binary patch literal 1036 zcmV+n1oQieP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igP# z2LJ>Iv3BAB00WFkL_t(o!@XD0apO7+gx=i0*nyY|%nn3%pi~fc5TygA1EmAGf>0HN zsUUU-EJ1ESdp`#bMN_hG=H_8W9~5P92`m;cbzKJ{f>H|C>lOEGo@XefAcQ~&0RV8h zT%ff^RaNlbgNQ&xnCBVJIS>(~ltq680EfdNf_TJ22&n5CUDp8sgb*Mi2qECThf)ee z1n1mp|9n1|0nGCZDJ6&qFE1|-fw0y_r+j0+#Ov!ThzQnN0Dv(DM1)}&$^Zc1d_G5{ zec#^&aJ^npRTYL|h+qzf1Dta)>{@F8z&MVpbrC=gVjM>Rz_KixAe2(jT4Pz3cw^?& z)-%uZHh>&NDQBr^t)aEX=jUfUKx-W%LPYT1$B6KL3W7?GIb=eJ8^k$)^mZII0P$VE zZkh(hn0){MFbu&<{Fe4Y%UQe-#tA& z-K0$j0p}c~ln_Dyz)BsRb7-0-iWu|z6etv#9 zHILOShm;jFWpatYRaF&zYOPo0T?CM_G_Q(#hXaUWWUXEGhSKlI7!yk-1;A`&#-{tu zxlM%(A;j-O2$3(ju<`F>Gb$aF67qC9#oTj~*=thVSvhiCb~$h=sT-5Wd%r@>WGn$# zmIceQ#7l75=Ih*kQNe@|)V3{c*&pt#tg0$HolX=&ARz>GT}SWl@2hpm{+p(W9yRA2 z5fL4a$Kt-VmWYV@z9%B0VHo1NuIsW>DdkPMwQXCJULnNhXvNCdG|j3K?oC<5AMt$0 zQk>>Cgm5!v<>2bNj^dTK<6QvGxmYH~7)U9hl*0G-H@?2UqKdJ^?zLrWZH&a$2(~#B z=5m=n#u!{Km)(wOj9DFiPppb%$Rjrk(Z|Qf%|OEC1^{nwZy+LcUAI!oM+aK~pb)}J z9C8k9&4nDX=jZ3uWb{bbR{-j|#xza40P>lU33)soBY$`@$^oYl+pGe1FbqSSbV~>4 zGa!@GT3ehQ?;Q>R)gMPUN~n~I>ktBk5N`Int|Md2w#YnU0N|WM-}jr%h;OR3#yF0< zlk(phrQu5dRP6EKU)rh+r)i2&*b<$;$?qdpq14*`NBa#<`c}ZkF07dV00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFq$gGR5;6H z{Qv(y10{fofkH6I3@AO3$p*x`Nil#0jeqs;pT9Ds7{CaN1)$9r#n~kE{`~pF@bLXZ zhF?E_GyM7i!oL`P0x_8Wj$ni2F7#hzWPxfvDaIo>#A+qW*AYQLZl(!&BX$x7Ik;qO170ssEM z@$bKXf%rGW?|(r27bf-TSv zD}TdX0CM*JhkLO)8|Y^+n~Q^sK~hqR;q|N647YFGy>NTZJsWr!5CaSfwJm@a><8NX v2&h?|6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000;<5v0zO%9O+HCOhCe@lCtqI|U`n(Bw>E`n0X60GiU=_L{j`ZeTrWl7@6TVgmzQ|3 z5;Op46VsoczbZwwqJ7S==^_3_&=Ox0MY;dOCY;|ap-3z08F!}8RFQf3;+NC07*qoM6N<$g0j}hYXATM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_green.png b/client/icons/bullet_green.png new file mode 100644 index 0000000000000000000000000000000000000000..058ad261f520490be9d3fc2e322392fdedfd1cbd GIT binary patch literal 295 zcmV+?0oeYDP)ef43{&%10 z`rmr0`TyJtv;LcOX%laN^>UMjsi!CYUwmcZ|JfI2{-1ED=f8fLD)C;hoM$LyFlFzu{izqk|8%^q0F(5@h6w@ zuSbE=i9QOwKvPc#-iPCap~BwXFHIr_gU^WCH%x0(Cm8h3e{9o}5`YUO%{ zPiLR-*D%CfK42<(c~V-?1q(}8{p2N#A`c~!wa4X-$LfsZ0%WH-1^Zy?%r3<3e~Rbycg=S_Egdz d?>~Yc*m~Z+JF!m3&mHJ+22WQ%mvv4FO#s^$Z2kZM literal 0 HcmV?d00001 diff --git a/client/icons/bullet_red.png b/client/icons/bullet_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd803115831933aa171497cfe9c1af983035f86 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}mh50EX6wkMFui zZg|fh<-*g%H9O|;u|DY#DW^u;K&o-|vHe`x?xbw1zYx$2><(A#;6QU!sSfhO( ioL~suuJh6Vfb_?jd)=>7iZy|bXYh3Ob6Mw<&;$Tq>~Ep~ literal 0 HcmV?d00001 diff --git a/client/icons/bullet_white.png b/client/icons/bullet_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a9af8d44bf3c001adc41e3774f526bd1d1448b1f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kvthf+1gnf`Cilxr3SC zCq+y2HhAz(;&}R`x^q^&(wiOs&2u-u^*?dO$=Q}CfYva0y85}Sb4q9e0M-pfO8@`> literal 0 HcmV?d00001 diff --git a/client/icons/bullet_yellow.png b/client/icons/bullet_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..6469cea7e99024577964e5c05a3d77d9200f18f9 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8^mK6yu{izqk}bDmp#c_6~(fb3t z9fO=s)r1xNys<0toy$Ta)Ef4L9Xj#;LuHACPs?SaC)p1!HoK`$|DN0S(B^2t{U;k3 z{z`Gkym)-$S?qyE;12cK15evqWFMuk`FjMfG>*N*A!+l(#* jF-_{7+4G}*QQ$ohjSunXc9>@Z9nawD>gTe~DWM4f1nGD{ literal 0 HcmV?d00001 diff --git a/client/icons/control_play.png b/client/icons/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P`EB*FHYdKr%;k=xO&(k^EfNlSiKZ>5l+xr|%SFOV@6-ysFmD2F5 ze93OiS+LaQym;|2f6tbH%~V`D+ND?vc>4J^KSLxEMifJQ`8>*~y^+pGr&o-n=LJ zGWB(yB#;DR8&Lhqi{0(#wc#SwSB~jZKzIFx`8od>2Fo-Pfe7*M8^q#qw2yTxiXzd~ zRaz|F*rr78G`JZXG*YX~5K@5k>G@0HdlBo-6v1jHye?%qRwO@+-hO7J^4LlPG>@A1#{ zQFl4x7tnG)+cz_2Mq_f*H!U)kgg{iHqxT)Yr3ec@K!`)z_%h1c0Y2Eu(dMPkrhq5v zY+bKWfx|sTiOEB71HuwS*CDzA%ReBv4*7Zy7RM0n6`5#chfOJK`ze)Y6>d6Z?UmyNHH!3DdsP-ARyDo}1HO+>7_um7 zx_gj{+_aU_OUH_~Jd?KI#ICZujD`of2mDpCv_zFGE%6}tfL|j!WGu_i-u>4%{%d{$ X7`zMSfT21V00000NkvXXu0mjfkBx0` literal 0 HcmV?d00001 diff --git a/client/icons/delete.png b/client/icons/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/client/icons/exit.png b/client/icons/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..2541d2bcbc218b194f79fd99f67d33de1873c6c4 GIT binary patch literal 688 zcmV;h0#E&kP)GS2eE@_I zS~TaE^z1tT1me$mOd>fuB1*9ukjYHe@2!~sjG5tP)N7xSr3G9P+3oKa++V)SLaGru zn`QvjgqvWRa7{oUyOUDA37GDE%9f3r>9Muk`Z$59p<W>iYj7vxikNWw_8sK+%_fnobvCa5%KNOO6e%CReDLPLmVwdHp%H5J z8cVW-n2=oPDz8D+5J{LSmLlCXMPg`l3MX6KVJNMw&n~!g&9zA<=CHFVyLz1l^k+DTiboyDIuKD~MJE&R)Oo;bO6gPHCylVLL*a<{&sTsdkI7k&ZU WO{4dBd(FK70000?n!- zT~4lR-Pg7MSkL>e=lQ*F-u0~YthKU)vU8%ye<9zMgZX)js7AapLE|j^=J~vJROe^I z(&M?NJ~7W*M-2>oC5CToECKAt6z3kP?=JghahTN>~yS67>c}Z93}> zdbBz%E>120m`o;aYYJ%K<8Rg1Y&LUSQ-Gg$1KTLA#Gu!q*Oem(0!jx*7aKuPeuFGNDpC0btN;(d)D|&X*tv zsGVG`d>ajV6n6GD)mvA}OCMl1n^7q2P&znT>^f~307{kGvTZczYaFLcF2_ObT*YS4 zYY_yQWt^hfj9yn>B}Q#9nM{2>88^g8U7D)c&S6(5gV7p2Abv9niVuXM1fW~k*AR@%-DH18Dxz&|o} z;n%^H@E(DLbq^qI=LSo^HJexB*TI#LIDbOo{8_PSsm%oM-nx>+ZtjeXb7M#cJ7$e& zhvs%Z7fu}_v70;hHMcOCj4Yr2GLv5pry&0diQU|-ey0xYsp3~OoB3c$0w2CT$R;|^ zUee+odx48NI_9mtjeG0`Ts!`XqE$98zJ86ng(d(pkCf6N?jn9&FXHjS1%}VOj@gDU zpw0VB7ZSULGf;8xyck`jXX>pMd^oUy}duExe!Jt18^ zf1Ig3`S_I$N*ApmRU4kPv5M4&?Vm|hJ? zTQ-UBccfa4bH!WPYr@)g<><*X0O$<{MoyT9Z$uk{qdU>=fBJIZP*$DeR%g!0Sj)N?(!q|;SG);8 z+VaUPnc5G48#xz9N(f@@yy5(~H{EK!#`g)d@_Qra0!e)WIrRPCY^L>5>Rb{o`Q$x@ z4H!w`NiCB`PG##qwqQ1!`TF}EyuD;9OW$6}t*raJlQf@?zF5umf_$5a_VNoPEwhl7 zJF>ZZE0_KM{LIoOn$4uXo5+RJhnSn1K|qrhq-7TJ={^krN%PZ4%Pb_i)1NH+6c=fj zJ+dPg&-`RFjn#>YP;u|42u|({;Y7BU?cYD(YQCP{<8yhXmkYNJgtKRTAQ@Su?D?_8 zrm_1Box-R4G>n=3F?+YKC3;SbFU$!Wfn4a&ISaTjI_)` zKHhtyioZSE*3dM%Gwb(UC;P+!j_kMDyP?m-(B#Ez`uAN1k4a(Yh6R)s-?y|~|Lr{Q zO^l~ar`{x`q!B+jnY7G8UQ1epyH^9!G7DLne#**c?xi!76y2lnPQ@HtK6iw$79DJ+ zxlOAU+`W8?yt7#Z2L`ZlOF95k-&sJ`u@ii=Wg~fKvuNEal4WZ@MzrC3-hGEp=hJ-} zM!&t5-CI|2=f*Wl+ud8aEKKT2NV6EGF4@V8y@!#OS;!l+)*)Bek(OD=y4|@|{GIr5 zH}l`bJ;bGWHz!mR3!p89C@L0E`|y zmeGU9+DtHjD2nKH<&>8d15`P~f4<^P4rlD(%@4K{6xIp=M`t(0%F7m|gCma4ZdLs0 zb>;LM@fKIIk8<_=ahqy=h}kSst`#XQHzNpOFp6XxF2!Vm1wG0v#%&g9G%@PB~uc9q0 zu_~jU7bc?tZD}z^qXDzogeX@0%{2viES%62rAkfm!Y#;Ta%A@M-^&(3sBxU-WyQ$k za^m_WvS-^Gh9)oOO7>A=dk(gl<~te9*mX8QStq|EKKT&wyc@NsHGeHpc3xeSEh)q>#Ygw&t zx*!PsY9$ERwtgNH`Zeae^i}+M;u4%(JOH?O<}iMZLeUiD@zW36p4#8l>|=y9jUlE> z0%wn8V9;y1k#iCMporQ^dn_fTWgIziieo2FV-`iO<>X^98o6Ke03UBJ0QwCbjZ;7~ zoC2D0?$?Vpi@kg6Dv}a{_*?2629BCdgTOEVaxR_0#mx(ywv2!8+VBJ~zZ26Xfd+xK zJK+j~FiQ}GIn`{h34*YxnyrH%2>a@kuuLWsqh}6BAy=^Nue;dy#UU(}3q-W__xamh++`TPrGd}z~$?tCFO7=0n z{bGep<30<~O;u=5G(&f?89!_Y!o^8OY?K2enEHH7cdSL5XuXt_ac3z`H6@p=H^+}=!SdB8+K4ieinWX=0;PZnI7?Sj!#qJ*z!Q6Ej^c^;h^p9p!kblMIu^-`lwrZ1k%;E4k$ zEh{0YVQ^hS)r=rmE>o-H7Z6HNSS#XRO=jErdECgkV7s`_fJ_ET`>G4QTYE=F^mC<8 zQZBF0zQLH3oBZo=384YDeex!kE08PftnBXI{wP&y1}4tJ)zg>MltfGE{6}nfe;n6; zt5{=e<_+ig!GAt+A5j#qi=vn!ilX)ro1xN{TdnVEQ526od1O_Q%N15negV811R<9z z7&@_{tor2raaMh5;_|tpgjU|K>fV1ed$)gN)B9HdIr-O_JS&BxZkLt*hnJeks_QhHavJXk~YFb|o^V z8wxvnBBEYECRZR=C}kMz#kk9%wu)rQAGxX&%$nYQJ0gS7_F{Gp-)KxVU){5ZV$i z-+-FJZ;SNj*IEtc56HgBIKZ!_HUWr;>V&6HBdfN+&=xcbX^v8*COD!sCZmByjhmrz zsJNQ-@(UomRjk#1B}E!q$HpTF0(SN)JiPshY}*Z258o>NnmB6i$OO^nX~rN30#1%< zy2Q4}L8Zda+Y2WrM?5{;Na)prInyR$Z*PyEuQx9z#N+DX%!A?*`uBc`)r(W`tt=Ct zhO1?s1tw8e<9q>xMz+Vtzj0N4fPZiV!QoLT6m~Re-VQ_&Z~tH%o!t=tH!nY$xB27a zIU?!>+&mu}y3XrDkUlT-^hlqVsWsB)Wu7C_=Vc@$BqW|AQo@pukf=9E2}?pkqTV1S tEC~sTdV`d(BqSv24N}6AkdUZ1{09T)%`hW*ksAO2002ovPDHLkV1hWvtV{p^ literal 0 HcmV?d00001 diff --git a/client/icons/logo.icns b/client/icons/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..259376a97074a83086b44222761016de8573cfc0 GIT binary patch literal 35391 zcmeI3iF;K=wzu~_Ck!IWkc5N;*gI$EWS$@i0Rk96nTG&j6cw2wvj{YTz|BY?ga82o z5rWYmIE2P-6mBkpc0>g(9Yh8jT0t9$KqJVM&dpTcTYI0V_x=Un^YrQG_B|)3err|L zs#>dRzwDkpeZgWy$$e_};@EDAV*8)}c%_%96OH$JiK!ccsscs5cwr3xPi)KfeD$fS z`2VJH*ejx;@yC~!y%gRjY_$+IBBn&tSBLeiTK#sRcS&R2ca1(5MdgCeKFKfIFl*)+ zXS-S7`G`>`#oP&>E}S^pGt@awWW+5JwIXU**nsOZJ9pTYESVYHD`=&h zdc+%|{!vi;_18+bw%aYI?s-A{bza4>yzT3!Ppb@gV*0mUV%P~WJLQx4<3_e|28
    G!v_cV zyFT5|vp6S7lT+{V@Nsk1M=jY=9l_K$&D=0ZPJJp<4<*Fy&$VLeK{sapC?+xWX{)WN zKNB>vyEXOx*3@5=tG+L;Kiid6Z&@IxKDtH3o7U8Oc5s9mDZftL_;u=wd(5A!C~8ab zZ~mXz^JD>NwcXw|Ks%i!{2Imf$BT(p6>b0d@uV)n@%;nhL~S~azl8fOmbPtw zYW3L>AMX}XM)bl#hW|&Rf!4-r0X}bt#x&8k;epQ-|A5k)54$9ZU%G@nwj)C{YZQO= z^ZhbIbh_@}vAEN{|EYKO6#p{(vs<1Llrcv3j<4fqM)2Yn%|8|0J`^G>R{W3A;giJ! znDC7cj@OB0K7&PlgLu{Fp?JHes1NEbe$jo7?(f)8H{ZwGv->U4uS2$|yWizG@;YLj z&_;?YeqI+>ME=m*FW4E>`2|ts@I5EyKY1oiJo!_biJfPQPrUVY_AUMafoaC$jtN4% z;W_+`mLznVXni9gAZA`lXUAhpMQ{+!LEc}8{}E3Ic#o9Z+P^_8@E!h?!|3~iXfS-7 z&LGdTLj3DM&%mw?b?L&tUPNlQYeIb4^kP+Igr^qJZ$uye=FTujHPx3?zH8caNcP(GR;u5V|3OQmIM&hsPLjjEOFr)aMAVz|am)WwiQ6h`UFh_ccN~w;lfvQNLDrKIQGx$uH1| z4(Zh1&)+j>Hp6Jh6>YEWpQi^pdnENAIdk)WbGp{A7cJ@=#pTDtp17JR)cS^vG6su` zWumsezM*lGut#?G|8Fx1x3$>GaA!uo{X{>J*IE{RaWBKm&y|GpAG=xKv+dYwKi_}e z^3$v&oY*nim4qFZ9Bm!=gkb+g!K$ zM^V0<93mz>g3u;zqCgEvsuP;c#AAP)c`I3YI-NnB&pG^BnR}${^>|!S2 z=jtaS`XJ>#TGVLmkG`D5gl`=9tWGTPd`7O$()JIg^sQis2oX|p+{sytaJ5B_3p%jv6S&z3H(dPA%866&5 zNlT+R>g98%PnUoPvaL@1eVXoQ(b6$pCSilP*wO2+gZ%@3bS2>#^~gV6N%)nI_I?i^ zr#;wWN!Yucr*8*ciwN|6(%YwvBh{6J|0tW|uLs2Sd1~0SO{acK!b>fJT3-1r z2^VNfeoextE}g8Cu(0JWnS@bqx9KnPo-mW}ex|p7QxdkiS>LN&O0~b=KX3W#RuWF~ zi*Y5P&(fn$INNL<^W+gRxUK6X?BV}ZQxbN*AM9v%Y;!A;@X0x@B#iFJ9Y!Xhdby(Y zKaY&}5A2rIrJE}WLpxYW_*3(WBVO%2BJ_y)&*;7%x{|Pq*KSu5dQJB2^486V{(Z$S z{=v<+TS?f#+e|{=4!b&%gbtEW_s#NXO2U0!nw5n1Pecq!7#t&NbkAdpeoexqUR+8W z#2-9K!tSCzu$TCyv)9LE9UAK9dbMvGdrLg!nMD%%Kg-q&T`M{b7vK4`yRba$hn_xx z4#V#`QKfl*E#|f|lkoBJeyQSE`_4H^fp3?9H2rZ+CSjW)#U4Cg$dfRxOXQp}ep>Uz zBG91OXeX1ff0y>dT}e34duVS>Pb3L-FNecuD^J2dCIm#ek}$l}?HZ@IOv06a4Q<=0 z%TvR8`Pv-8nw5m@`ib3Motu(yf`5P!H~B5m-`h&UDQycxZ$FBnbrSxXgx*#X`ug81 z_wxL|N!ZhACE*nBRMEZDL~}8tewT#p$D1eN`3^pJH~RXTN$6=Np?MOHZe=B5qF45h z9emc9N%)F=Oj8oJT*hEX!Vd8wSaT&|>-Hnt?kt(<(`pqhB%!zOoj#hQDG8?s7>_+> z%y1>4Ux&XA?&N4X3D4MmO~SLjdRb53ATtS{``<~}E_SuieyEv*{y~Suxuzr>+pgO{ zPj4#;1A^N3PH$l)p||ezdm_a;3H!A7>gW@oM>;#U>EPSeXiCCsWpg?QIO6+0HEjC- zO2U@Gt$vq;3-zT?QK?G$B*rKWhzjZsQ)Iz8jor zD9iGNay;Fzg^J_ZhB7o$DD$%nWynUc&QNmS7V0NXL_70@dOSMaun!QTFS4o(p`7LO zRKBnqMz8QShEn#nPNKGw@g=f0g9m_E z{1oJuGhb%`jBw-?N1JMI5eQqb--u<8-kC%f1bHLb;bN)S475o~y^= zo6rn%2MxeVoT46N@kSJX(O$KFoC`!(YR2P7{Vn#Az$Sehgs@_n?LZzD(aD z)Qk8~it&0U8n5H%{qO4ydnrMY^Q*;Y8eal`MEY?c(m(AH%66O{!C4W?*cx$+PA?!l z#pj-_{LH9=D@ui0-4mCi5N^mb>>r7Hf%N$J79 zBWPZkC6rWpj&IybRL3KXyYdz>Ok&B9e24Oo5E&?wQNB*sm$nJ@=e`L0-g-+YIdnaf zuIJ|(9`V&e9B!IUCDM{Yp)*EWqBy`0^LU}L8P&U6I z)ce7-|IAuR-T5?OzfrcR#|}f8@w!mnMDy+&LK#HUWlLhe^JxF%U4|N6PrJD~CwB{F z3W6`BfV6Thi+aOQ&h8Y-CPp52tyn0p(ENdH-fO6<8)-?WWyqsl>?vA`Wy?0B`NI74 zC9_hZ$ki8bz0S6phbUF5Uq_Ui#%7(6YuMxG?^#8R$})tl@yfCsL+xIHxMmWw&O>`3 z!|+%tYI1{e413(%(#tHtUgUd;*1LFiet`Ji5V`pn|8XMu;)wxk412jeHZDC#B4(iJxjdMDxqtb-m&^MBnQGGgkP*TlsC~`?uxGGjU7VyV1uFD$q~v$ z6kAYS$YX<}I2D2-xn{dicCIy)@N`0i>ISL}sID=h$s}*r9~ssG{#b}`4c$e)SA_H+ z(i)^WNbeJt{aetc$R)@{TUbblvkm*$@5M#LTh|IDoU}?qT#buTAvbV7V`xl8eS~D0 zl4&$|ZsGy3v3r`KOv4}%gB6x{`|F~9NIKbc2a`v`7j*d)Z6Ksw*FQ>fRwV>93pwZq(8rwHtRnm)y(A13oLxy?XWQe{5<#8zWd zNqCgS1?IL@LuxR&g-JRleK1MI7Dm)COf6&4PKnyZmIU(m~81|QL3r=cf zD9&qxvJH>ELflFu`JWZ{82#6=+c+#~Yj{D9naH0HL49NclOKvI{>WdTQwUwY#}RWnu*k55Y^cr@uTv&( zvc{DZz0DNKi#s^S=Kmv`1|^+!n@)LLN|oAPXxN@Ex|Jh}40ReTQ#qe&s4vvgGM6A$~r4xOgTJ$;uVmP_4eQk$p||AO15% z{~!Y!Pslh{l+#k6M6&>2OrU+l9ZJPw+BdT{^|G4xY_t@4!uIIkc!FKFi|QNM~$u6OE9ww&6BX*V~Ft?ax-`#FdnF}nxhC`xg+6?fYikYQZpdiKS@ zGQ1O#JuoA;IVBfP?Z$&nS&mnlJz{@f=t+66o*sd22 z*=2Z)E^ja|HjCuq;B6-|&Q;~SlhlZNNbtfz6fNyas< zL6)G+9Firt<5r~JkmqagO{qKjn@+hyubhu+HD{7?25+nIwtze{ihrlNii(SzQiw>V z<8>}+N~KgS`km?wsuSD{@5LfJj<1!dQa7U-@H^FEGC8Q4nQTHe7H8kek%fG}SuXEw zc>|X-zv-0iI6Q*Gmr%{4-)e5o$}8Ih^DLz}nByZ#ccO{R zQ2J5crRS&{_>=d*@EcO8hMuPkxunW6ITb3_m2h0j!}TLN-cl%V(DgKX$<%G4*t16l zH89lLWL%B_wX1)Z4>BWa3I$E!pHeVQE!9FM(+6~(hcb|VSyLYjL>coB_Rv7I!TTx4 zrj{M~k&^)|U;roOQM8%?%wYgW*zebSV?O_9HoU2ohcoM;!Xsa`MCA5-`x@fPD)z?bk#jN@bwCFpDU95I7`-?HTEgze6TXeqmZ zUfv4JkYA2PlyHmN$;mh_xg+-1n!Y*vudm^;R1H4DxUb1QwG8$5J^0Cal8b&-3Pm+e zUd5v`g}wSOnS=Pd&l$w^jH%hM8X<1sWBcqDe$s2<^KD$=EQF?Y`t@CPz5e94-ftg|4KhvCp`M2us58} z9moS)?3_JUisaEv`zj$$#?n6d(LqiY4vhH0&%Ltd^#kV{i0LWmVNs*_QwWOZsy}qa zmf=^hx$JZ6#a7}lwdNyN!&^cfa-AY=LQynlgeNcbucb9{l>d z@i<60S|+ogZ$(oUuz&Bp%1xwS$tdjS+}SVK9JApC*6a#}t!8&TKTS%dIp)9tE~==V zORhC-;H?}!dlUIBXh0N_=c+ycn%F|pBsVYvMDeQKpTr5>bARKX1nczN!5Xt+ldzpx z%{|K&x`9>7Cy$rOotP)=jh73b-cC+k`-p>Ejp7F1tp3&~n_h_K#`El{!(0klhV&fz zZ0hus{$1Hp&iQA{#m9y%Y}G%GaMv$4Y!ga~D$G6YvTgs(x!S2eMuZ8{RzijEfgL~Dm=_|GzxqVqEBi9*E#%_4Mq;$`|_sR}@ zaOm{4+V44G!nhmsy2F=nz6`u8lyQ8%!e(Q^F@r>mIU3Q{I1DaB0?5{RmgP>9VO8=O;!WB7 zm++}KUqU5(s*A?Igio_5j0>^oZ^Ea<3{4H-ZlHeN71yuQCdX4j7t66s{1rYWV==jo zJ}x>l%ps`Xhw_uTiOyk>fyHq?P59J{xh`k;nGEMt2KPncRKQ^RGK3MCEKV$6rc%T% z;zQYm*Eldkr)@Yb$5|oD=o)d9PA&LE7^!$W z7?n{`%7GS-%i#!f^ECTGaZjhueRST~l`nItaIc^Y{i>9Ebf#vn5~2Xz9wCO%ygW-N zGwHdT37^Iy>?+|?U2m2Q$#*Dug(My2NR&kk;Ts8`5)md!_>@i8FVgj#T+O2!Wa$vT z%;{7j-Aw_#6oz!Lgij|jv}QpPJ|z>Rcd)sQe=OginEJCz$Xl8owrd%3wA& zBUv6&{gLfs6e;u(Wx^*1`cw&@79wAcWKV`>n_2M?(W_Zn%K=N%a<`SU#p%d|PcpKv zAwP&b0eLx}Jf_+%6%Svpsgb?W?c#>{PL8H*l<>)*-GWby6!=v2{=&E&Yzl6*C1~~m zpPoi?iRHwV{m#R(iMup4LXwX{hGv`qK21V!Tnb35fF)sXXv$YS6XeqzBjM8mnh(h4 zy_&jG;-oZM20h?@o=M9t*|JS*K0hz*l^MxlOe-TFXItaa^uu?>2YO zN@DcB37?kcXlkqppT@7zlw7p?Gc^7wvSzI#N3+MImT{2XxF0UR>oCEvnO8#KG7?;xZq4pS&g_F z7o{SLvF9;{#+j(gNtQ{OTJwPXI^a`mnx?#fK>`LcPw;Lp5_N;p$tDS(9u8sHp%+O- zHi4Qb;q$^YO%0OpDG8fbu(`WN^T-#XQm6BL&im`J*d%d1wlG%4FFGh+0b{pG_*9`|eu#reRx0Ibd2l4sd&m_m!|`y4 z&r&?>!^3nuL`nFxHk!Q~`z4)hWb6?VK2Z^s-K_CD1bei6be6k=mn4)tjQ@;re~kNc z(40!jV}oxo;nPH{lj|gWiep7WBz&stgDxk7VdyxJ@M#$iP7&g34q6sU_>`t^G*?)? z#U@bJa&Ia?yG+8T05ogq?Ft*Y7USs{UuQvAGPzxp$g4>OL_;24a^5Cke;B+t*R>G%)beO{U zYer;}W)ePq8Ntc19+Pas6PBel?+SdY; z*c@MRpp_Vu_(TFr^Xn2mjU}r2fplrYC%uT<(|QS?D3cOCou=q*lzd}{W;5n~4}2nb zS+}W_$5*IQ+X^*Xza0`jY3d6SKAp?e)F~1^jpgJU(LWenFW{4A(`QNel**h2oA8Od zSW82~r>_pZ{vr{{gdCxIO~R)=RR6&eKK+@2jU{9pD<*u>ln55!cna-9Bz$_A_KmDf zy{zW(5~a#73=kXyxUq2X}us_vMHA(|eHugHbQV=S;aJ*m7zw37<5rfStE+KL^nR zitB?A6mR0L0C(FMkfvQsXaDyv<2WTaZ?o1WeBuVGIwgG45+r=u5>%qulHQ((bpf&w ze9CKV1zwi0X|?6qJc(`@v`h0Cb-2O2*esNbgSR4NoU6(?DJud#UCK{lZIZGp)2Su+ zS%Ppivvip7$-rbZ593-1@abF*6SIepE4W0ckE2~K;Zr1Ers7q`Ucx8s9r-MlskQDJ z5fU2TKY7Z${PG_pe3D098V4j>jUq49pX7lUE8&xNK$?)r5gbj@N%*u9Z}Z4A&4N#K zib|HGViP`T$~c@|=aQzJma6%`^HquJO9`K%@Ku4Y<*25ERkWnvsSc6J4hf&~Q6=N- z+dxJZcoq1hX%>94I+aCbq=Zjl2#>PvjpOjQmghH3%g_653i8ZM!(TSCVf4C>3qY)d zPns5O!6*DdC$2KQgAzUkAuPw=?MVE+4^-mNA?M5pqbo-D6O&%aum(x^G#`f}C4Ay! zv)~gBDJ?ZU>3O(>Pt3rCPoL%x7T^Z9X_7 zlkh2m$PAM3X&*g@{Y2059vD(3m8$a`Wyl;NXZjL36&@3=gy2#ht_SJ30Qkg~u;3Fe zflr&+BLhtMG!mCXBz(FoA7q9|_@t@97JNcWwUEhlfX;JK>Lz^Zk22zW_7InD)hMGJ zoBgvR4+b!Rc?{r$Jc?dp0536satWV$Vm|lhOqW(3&a4Zxi+J6WZAlV76`@{Bf`7+j znA+`dhD$BsQ!>9KNcc35FTvk1;uG?QKVHJ8vtfLjegX&PKqP!xAAu}H!Y6qvd>{Fx zXhg9RKAni+k~^df_{0n)e9GjpR5i*O_YdKWdnxLxar_i;f{Xt3WQuBxyoyIi`1EPU z)BN455^?=#j?*E)r>aE8UHB%4WJbQ$+*w$dJAZUM4-GRUd|Hku{0i`?e~!GDmkRZ8 z#2a+Ci>%zL?k@s1D~8ICR^}{5HS3TVD8MRb)tYcOtIh-4Q~mxoKj*> zYp7X5!_=BRP4J0ELQ_I9=Z1VyMsI2D+d_@A;1jJ9J}sg*=fTEZvbVrJz$eYH;1lf< zJ`KY<^2E(&u}+Zi2`3hOqMao@9z%Pt%jGe&Pmu75CwmJ%(GGlin9hc@y>MmuI!(=# z@QE0&en@pS_W(Ptf}_H26z%JVgIEclIL9lvHkj=aJ}u=Y(r5Q@>|c`bNmFOR3#?`d zpNhNj)A*S*N9;eqMHO|xA`3n-0tuh?B=DOfDGbTv>%b=#a+L+25J>n`)|(SLzD&X= z%|7|hz$Y3cd|Jspi+cmBlusTnl|M`PbZJY!?c|h%PhgG*CVV=UKP7@2&qN8Iw3g2J zK@+D>O-_nrO9jmR3izbif>wTigu8yZW*b*x!l%mY-Ul_?s9jaSr*pXj$~1dma-j*I zKHK(EVkwutdBqld`m~_u4y}3DAyb!cE|>6W=sNAm=-i^>l09#~^M2WZ4^LU}DTup4 z_d6UlA$;k7mw)8pa{-@Oe3smW5Oigc`%CzAkf7cahAd>Lucx1Y6?XsS-Zv=9^CwaJc~Zqz3|@blCIT5*Ar@TBJRCF(ZK27-4jV}^DEoS_o)|*ed1)p>~uQd&&c?H=u zlljNG;FGR)Gv9pb!;+bAKIyzm#T`v|!6)5%^Qku;d*6DCiPN=&PxEqhk605vxu#Re zXyuzvm%@<_k?`qchTbgLf={{&KIs;G3c|e!pPXnd_!NU*3qCO$Tc8P_1|ZwVc*ir| zXcv59^IGtUsSy9Y8M+HTt;o__KD{(;eIf8^os1mS0Y)tMH2 z(oOjE&A43M9y^yepGZCvJ}t}9)i@JAO<>mRc@FtwhVHRg)U0#lu%Bj|@M$k{`R3C; zJUd+QiOt9Oj}yt~zwEb0x5s6>;FE4bfkW%qrzU*rnax_uH=jOZq(N(#o(n!vBJlb7 zdUgOc|56mX?iPH~UGRx4L2)6E4UXbe5DFK3(v^^OLS){^btMngHHJSigXNL%>5u#& z;ZrudhVb^3A8XG+mjBK>`NLF}P1KibVaObh60>pZF%LQzWF3Km-Xh8Zo;R&nDAZ<@QGS0Q;L3~P54B3O!&mMRh<@m z(oOi3h=~QC@Q%q5Tw3snU0}i|T}jRs%HT9yS@4N+L?2c3VZtZMxUv@bL@iW9P52}; zFAJMJv`F|A=bFh?Op3^bG5nrv!6y>idh5r6{=KC!%MCVbK@_>>;7QI~j8y~QSQ!6!~J z37;HTucNmspV+S(x&KFF z6v}@kd?KZ7i}wX%2Yk{GoDQMFO8E349gb)GHKVv2Snw&5XrE3Uyn5^UWt+ooc}+y$L?)wowB@&?N$&bX!2Ggio`X(-0FraTjYD zXukO*;S*U4IYMQ^r@d7FAr^dM7DSn2#e`3~63GG_A5Z&G3qEnyvBQ8*RA~vHP{=o* zfKNnI!Y6r+u;3Gg6ZoX-0V6h}GT~EdFymWc!Y6t15o=jUC4AEL(G8`M8};Ad(?|I; z6G?#~CVaB)R=_7NUlKm)x&@yIv-##zD7{+niQ60l(sdVn;y5L2``8^OeBuVG21)p& zTkt8kM7QPkIx4(mj$*_(X1+@QLEa*yk{I37>QeKJ{a5n&1=m zCVXOix7d|80~x@We54k9Vq*4)Cip}|xNg)$(fKsI%GgWzq+9SwZ{002G_?DG zH$&uBq*h6Hff7FH)|*eJ4){c5+0oUq99eHZaRPyrbPGONs;j8@S47_6&@K4HCC!9S z`T`RxS*l7@CVYxUcATr#3RE+}Dtf=)sSc6JF8D+NVr1Vs8JSbUCtbJTljUv|Gce&( zIPP5ViMjLqrt21bvUKvyOeZqg$VSlXJ}ysj5(B8kjk3qJ86WWgt@o`g?qZwo$|Qs5I);fdl(C>_hg z^}`&tI`D}tVZkSKz^4NC$kQ(PB#RtJ5%7s`52PXs1>lFtz?_=MbqPm$%LK8m4$n`hhe2U?c zJ5;{;gjm9-Y&rKp6Fx;SZu#cZ)p&k#NcdDWnxYybui}vwe0qkz6HWLuhU3%)pLAvW zn;eoE`Fiu9!ou|n#&qYQ!GupM5Jkv0pZZf)XPWRS@(sGP;FI3WA>Vu&O4|q%KBWim zq)yM4@M-@V-GonLXdGp|`4mE_cbf2t*SmESKFQY67JQ1`qT6~|@QK{;$TQ)Su8y!easODF}rNJ`n;QDJw#_GtaIE zKJmn1!6zEt;>`$3i3Oi@b=q(6iAF*Le3Cchi!yrVn@{l;e4TkuJ@CrbFlbR>Mzo8S}eCVXN;+NNGz&LuBf z!Y5+vf=>w8amN$sAh2j(EDqu%d}56)_(Zz}pLAQ_lHu5!@JUxw;RV1anoal=%YKRGP;2A}#I)NRQYe9C*eOt(8nZ@1u6A@GSy-*51#*ADJgL#8b+FyDL{woZRC zCa-9B$)0`hyubgzM=tmj%-ta24o6KWUj_i3Quut?gijRIyTGSzEV2ooJ|t)XCVb*a zLhb8^|0$+>`tAt0BjApJI|A+qxFg_>fI9;22)HBQj(|G? z?g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJ zI|A+qxFg_>fI9;22)HBQj(|G??g+Re;EsSh0`3U7BjApJI|A+qxFg_>fI9;22)HBQ Yj(|G?|9?ba_Vfkg6-8B{!TRp7tEZv`v@_{BjN#vH-_mStH6V+?w|9)`mq zb5$6PMp#){!RqQN*4EZA9*?oUzK)HJ4Qy_1Vry#)+uPgN+1bJF?k@KB_OQRdkAs5) z93CFx=;#Q?$HzE1Il<}aDbCK$aDIM{i;D|fUS47{nc(W`3fI@yxVgE(?d>h@?(T4Z ze~*WU2RuGLzD4;x_O8IY0{=h(|94xGC=#k^Uc;L%pkKSEo}w>XnnLGp>U^GKOlYkG zO6b9#5I_!8Oke4M6*eMfL^?~}>r}o{_$VMtPnJ_a&F2`8%=`GnEQ9Cr;mIj!7eiL= z`GW2M`1}Ik`jH~cC^`*H4wxCB5HM2x{LzPKMbfYY%t}Djv3^slRz=rAX@N3jkSweh z!omp|eL3cQ7b?X=($rr+ZIZA~z$UxSim)+O_VesYp>1ZsH4L$IJP znn6Fzjt7$)-!M+*Y^sftd%8kOmYU6oHXi#TBq-VuBRQUG;cUCf%|5*;Vnr?sQ3A gQ2T;&6*BTBEr&UnQ=;}Gol!bh@O*#gKyjDi?yfKQe~b|lk^v{# z`kHfGR5^p$54hf!qdw@R08-*^YaVJ6Ja{Sq&iM%MWNC3Hce( zSw_bV0Khf)?*=71sQm--B!Ro6w!6BMrMstzs|CQ*)05T4(az1x#My$?$<-?7T#yg| zAP2}wh-!G{o_@Ch6Mvl#-*l_AqRpy>#Ukg+I;t>C28*in#mfP3a73Y&B|+xu5*U)O zgK#RM*s2nuqKT5twUP8dSg2)`6FiE-jEX#2Y%y(U9okughqnNC<;<*eU7zoWqc;dM z9w!?D>mJ9B2c6O~rb2Oa8zh@j1n1HqS?jP?3>UyUfD1fNt|?wfW*)D?7J2y*>Q`tM zg1SGIFA`{++R>edRlflu?Ej_Hu(IKG7AKT?I0d*S09eL-?Lsw0E3BTnw>(I8Ct8}g z(-`%EDNk`IRmiG!eSO@K^@9Ovg;WN@C7v|(NiN&p&%-Zvhk;lNDex>fvMeU-?S%SuoXEKoYk&O|eI@Rpt+Lkw^8%mhm|mM4R^O`Z zeGM$Sf;4(jW*d=uaKNWRV+K0Ri5I1A_wy%<^b?TRLa9lclyG|C0AHj#*g{k;@lc67 z1GsERPf)1=|hv`jgerYgJPS3ns zgYoWL+(XT9#2x&0h)uR$v+8+)@$jXmOJ3w3s7tSdimx95?`c|^u)OIU8ipd-hFG+KcA3#u=sCPV##r16t#3kkVeeAU7?x5TbZd2BGaeBG(V=-$&Yye zda$^(0U49abUYo(CrBr#=D-pgM}i>1@E&f!g{P#j)9Srm)ghkBiYaazo&-k$y=1K& z6N;xn!NT{H2jWq4*PlEin;7QA15O=o3v_xm#DLl!7kje;e=UkUBznf|R(*-SYJ-KH zejgZyUcY~|(5uh5So+zJ15WI%iN&;Y$A-n=v7YI7*(w939@9PpF~-eN&CK0vEEB zKi7k%L&sWypa|t8{XmngPO0WB1zO&|o*oVQCd^SK=pCG|bU{}dry9jm0B+L1x)PdD zhl?N)Z~&BM;q3=wa6obbOug@(2!`xfBV1}JMubh<_hqeKQiL)-(FYS zY4dXeU3SmAok^Q=spqPcLUV^f1qi{87(xJ^aC-0{JS?ENRh1GJ$S^Mf2<{caX0R*Q zkQ`*Xz=A@dp1fJg%c`$#hzUaw?wSFR;Cb*XV$lgPqd|#KhJ#QbvoFUl`d3lmM#iXl zI^-885x30ceXysQ9yy=i5jaPwkk}qftxfcrX zZFogMZf?wud>`cHMw{_+&~DFsQWQk3V24S_Hmwe$58DG}&fmKyrX!lPN2yQI!yeT`p&2*uY~aPO8W%A7gOA2eBc>zjlI_acJ)p0CQhv30GkjQg z;tOYRWMuSRW@+T5Ac)7>`R*cei?@1&Et>_CkrL}9enL%6I}6Xd-($VyOYDf6R|JAc z$|%18wa+gY`ey}@C3m|rHEFn}kw06t>H;E6)S&4q=>p(o)QKAgr zD$Vk+A5`^LZRMKPF1rlyKsQdTNTHOgFLQg8Hjrud@_|njp@<*Fofa;FTZ@?xRBWK{~>t1=TKVg|C^rbz^nb%+0%du9Q z4XijRy^7FH`i)QOzaRl=@+#@RlAN!o_p1OjX`*YclrY!V6iAFP{jGA-r`2Pi9)IIi zR!=Fl#>ED>s?uG(DbTXx=UC10FY3MHNoxv#m8Hg7-&tKDa|m{M^>5N!3TTXj0jQPF ztzx`(EW98pAR45*&o=wh!bSYV&z-0DN!?y6vhWXdNT$}UM5V@zI}Z;QK<_~y(q@6m z%NXU)KR-F~{TXz(5cX}4h}s%aewpL#M*dIF(2`-_Jf%!vb` zT~!b@WOXp^$iWjt+GmV~r}^JDueOH`Cm2MHFVsctBk#L0i+sT&ZqxmJn zx83G$QIsR=6!iO&IEaA4$@?=$wb=n2GH$23(<S^mlycO zRg8sfOvo<(?(UC#C7R$u187u4oAa}js62||3|;+#q-g6<7XU!^`^4UFU~f}gnG?_f zwniayUy_nzKX3T+Vxio?&nth1W0yt{hNwIDa*^&ZBq7I#)`V+Cg<%k{f9LeHcOPO% z{<6b23~nt+y2DN4+!or;gysb6>nyXQ!O1>Y-%gq?W#fst}aEG{Y2>x zN`24ta@u-9YB3{qQDxbGZWG$tDX~5XjH=mAA1o5a>ZsmO-k4MWoru0?=b5rP+?%Fc^& zT6t5({a5K5*MU6;y~$a1+V+2bNorRcx+=FX^80E)0-%BsElsev>UX+JJ`gJvhO;o$ z@Sx%i-ra4D;ZSOjcDe=Oz#D+)hK0UWUcwRP^8K=j^Nf*LPK^x`kQpLH zvi{f2ak-4!0uzg+Ol9KDSzJm_rO7t5^y>LeVB4WdpNMUz~M zH}0xvV|4~bI%%rD$x%Fz;o>sgZdOpEog5z{$-%5+(d#$((iqR~Y}iqpnFqrEAiN4U zKn;O`MHk;X>~Bb!Uh0LsOHIh%W6$rF0iQw3O~k~WEE(x9L-kY_p3weI8QU`?eqL@x z<#}WRboZ2^4!5)f3fmJYO77Nee>j&nHW_fWf4`zd_s0T%gtOFePmS?S5+nVN6|M6@ zr|;~mXKtuQTjbW{u;)^ZsamYsby9%`<^6-{ip9)fr2x6fLiY3NWD zL117Y5k~JBe#??C{(Ij$_hasdO?uOJ%hobpXr|O}EsA&dnzxp54+EYU#s0I5b~FMO z4a)^{j8{bbhH8nBzItt%snCmze1B-lU&TXqS% z_dt*}y2HZ>%T=U+EG995?ayBQ-EC=>b~^Vo`94+ddtFIk^O!jY+f+7prNgpajfh;J z6Ff_Bms<0iikZ*JR2iX+psl|$tjHx3)d0)4WW&F(kN=7!&knfjKWGS;*>g^XFw4zz`{wh z0WYDTGahz-bqoc^zyqj%EX-pNll(XYV^dy1HkcA1BV~r5>J@WA65KJt%pp9}jQ=jn8{>&3GP!F%b!$r%m9q~8-8NAZ3-&A} zE;U`yhO}G^Tlg5m2p0p~-#?ukY~ACVnH*P>5>K^z<|X1vkUZh0n8@W zh%Je3UwS?Uv(@gZ?$V9eF;>lh`Qrv?*Ay5HBZ4muBSdErRFJ^!MS^)ZYx|tylFJVu zy*)!S-)yx>r&*W1AGqg?`~Y6%d^RQqTnb}tniXvgOf1#xT(xd$6qobG--v)%5!KTN zK8yR;=!eY$I=4I%C?{K(D;bMNZEf0uAi1n8@3uMAFJ( z-JirZtKYhr_y(p(!g@-guh*q+XJgWp>##~ZV8H!;(aeQ~C8+)*JL^{t1Al)zeFBgh zCu|z`#p;?NLK3OXr8i0-8)Z!jA8OO!z^5K)G18Dro8mog)CWbHYbnv)2^s3^@RRl( zD22}>46r3CX3_Nah{p1X9Sikac)cR-CXZ!njLGR9gi-kjxYUkv2d5@Oxm**oh*zuR z1ngxkwhPsU@~Gz77H36K!~as&a<(!c_1pa4scp+sQz&KhicR@4e&%Uw_Y|I_45%!9 zJCd{)I6ZP+=;vv{CkfUJ--f(r)4HW*xf6e=WxSqvpvWg}#qBkP$!c39UAED@r2viR zdW`TcpC&!8O_jD{HL2~V%TXt>%Z1n-uECSSV)$hCs=(f1Nr}kPnXbu6@fJ&~@lzS2 zIUU&U)CtMFQS2$E-h0Q$;WKAG``OGPY{|s8Ew`q-#*(4&O(YMeLhg03h0oHjxxJ z%QSQGkp0~T)a^P;0RXG-+ugd{fv7sh?g|#PQ>~?yu-GZ7WZIg{o?Nv$9P#j}i@Kq(4<11!@)C078lgnQNIGi@SUlQc;kWztp zd^gu$UZrWdf2mOrFiMUEfEhQq|Dlf__#}yh1bqs-CHTWI?Si0e8pYqd$4( zL6o{^rqji%$XZ8YgD^$$GP`EL%N3ll=l4KX!v@FC*H=t?=r0$j(tx2Ka8!Y`DA~%ok!}4*LC*0_Z(q>k%`ntLMVdF*E6NtttYA$ z*}#j*{iS?}njzDbn&E1ZbY7|La9BOBDR-layvsZQq($6fdDHNz^stftc>=mf{-WD| zam9Dw;D5;gbg7}yWBT{so}aU01_8iXoL0?riYL5-oh+p48e<&@gSwl<`gVIBKmc%X zu_dNQbkuytNSF~;@iPV_iHSmUOknZ)7$M~uLP0@sQ+!r*lgG?GT20>4V_UsXu^DbN zfNuUJ-^et$nj$0=H!;0oOskaVSFe0uhKeke1x^=Y&yJmk@}oe;)l> zHFS?e@(spVMq94nEiQYvx^g)ZG#M;H`^6Bw7$uk@@ts{YRO5}$i_S*OlBJvpfq=&e&8rOt;7a>F` zjQWE>rE}+O(dYr2fiAAVCO!B*s|0Ri4FpNZ?oiC#9M~Hdo(8b1&qnC4VlY{_G5)YU z_BAJ=`b1J3K!94?sgW3CP7KE4z>m6wursSl9e(ypc`xgeWa4T|?J!O&I9ih)7RwTj z*g%RcIB{3s8oT9<({8}yzA1M8=9kED!pOuRWXkCkP6mGlo?fjmssSQ848`>F6cmcN z!_SbiybCx$CXVf({^Q_33B01b`zy0yrxy)9sa^+%-D6v1wH>zAs99m-knctd^We=3 zBb>~WG!~Zc+%fdr5`$)~?}$Lw#>PP>tJIc=4`Ad04=JcaI_R&!P*L`@8T}4Vt}}Fx zbcp;I{Eu6qt8=gV6&_ttPp4Z(uUzHUinG~}kD4^3%$FA`@f0#d(tDO)Tl~SPj6kHO zb-@ji8p@16e@loYw6wIW{+{DR4Zm?Ydh|TQ^mHcq9?h%i>idk{TK}7}E%*Gd9hdsO z<3`~Z3oz!ye>NPi5AKB1RZ>k$!r@hwxXt#E=E*V8wh&G%WIkGa;wJ5)enSm?1m|be z$wcC%9A=m3pmHXum^kY%FW$**t&-4c8aTTlpB~ zvi~E{5FYl%`*%nfNq%@sq%syrZ;{ARSn)heFMZhdA~w=>rp@^5f3*VL73&o@n^-TmO>VC7Hj!f2C$em=NMCbgzK7H$WNW+#{n?swSucYS!Q5 zpU4&od7_E-&tG0IT@H@!e!^7j7)R+6PE#q(3>h#L$#PD#=zL@sP z+sv3ayHdj4h7(kn%cH|OZ>pQT`l#miFGH<1@pvN_n3pG?_!vl=3`ckEH;K<#ey+FT zJ&BR_L=M&|eBa>qzwR4E&Im7;lHwCn`cL(yO6rn5XG!cLw;kuEv9tOV3Ur^9gehmt zZD)ju`mcqb-o#&A_|4XfIKM^|40e*)UtA9!!riDES!+M30S&oS%VpLnfrH+)DIY~= zxbPtTok>!1Q?gv~hEnFjcv+&Yp7e7I0AmdFY{1)cmdfx+)7_mE_-~q>(ia;6ydX`B ze1(bmxndL-*ET6nhPpk)^7M=9vn&t|vtW7%6Gdn783sVUA*zkdTHO(VKBIKMSP=MU zG&=yq%J%&>0jlT+6}e=vxiNyvFhNp&6+Z8KU~%JWgX)2bDjm^qyV+L;%V)xoSOQKS zh0MAeRcAabLFk{4!%=fikU>A-Neo z$PB=x5MJttoltGbJMI>p*7!?E=9QrL$8;gaHXXJjU@RQNK6E?dRTP%4j#-RfTY|L0 zsx%@XFoq}FmK*?SVBJ2Yb7KxHzRHi8!?fD%?{j*7?=}ypZE;R1hzKgXq>}mBhG|Sl zNd%J#G^UK40Q`}Vp~Ajox+)_`7&Mi(YFO3^8PnoQl;HX%2-_{S3|*xb;zdR7g8{2q zIXd^1#X-Yq;%NFY});x4|DWk-Nm;#2Xe>^xhTct z?5C5z?mb62JP+5jDoF@0GFv=icn#{Ja3*?r`VmEuvkQeG$dh|T_UNlLWB&D}Q+9S* z`Bu&FqAg*nZia|2sJ=x6;2jZ`yLmEa{-yzO&ZcUNm$8{lvhmXH8{T56(0f?`4?oCj z#kVm91R)iN-b=J{%k%mXk70*RswG8elo022Ea`P*R9^!U@@|3iF-up(BaY zpi~Niz&zPpGD1L@68tH-4&8h1`y<(Px z)|(2E*5}Cc@OpJ9(8P_Pc1@u~Qb+fAl=+uK)Z*QA7&ClbtSO?=Wagzdk6Ob6`Y(Lo zP=a0=Y_jv%p19$$PNMv^fnm=ngu#n(}p0>is^t6LrLl ztx`^YD_e*%mgJgi^;QzoFX+dFTP7Hc($<~+#~?b9>A$83hYfo0l|=4{XiKQ7T}U~A z{ETnU3NEz=>^~FupV=Ua?gMY(qrp)bI2aw>=Z5oAfGQGn5?ArLS*otK#;Fc;5wb=0 zEtlS3dK|7^vh!Qz5XD-2$>*(h2&=g;a z52o-Hvn*5C=ab7_e#978dD_LVS9;iW93=F}6Vep9V!us$N(!zdnN66{uSt7L=9G75=3Rx3JT(Mu89k-E^(kxupou5 zQ%2ocWs}%bIs?H9NKV%(fKA;9MyJiyVoQl0pyDD1*>405l=#(RHrqg_OR_PQTb17Z z;=Hkw{&Bb((E>kI35Go<^6TnGC0eTw6sph|h9#HflbVl_miTRYZJh;pFg>>|)d=~r z;ekEC6E=-%w^QzBPwJDi>3Q^41jIAw51EQ`SY-p?yFAiDQGzY1e3ZjraF4!!|6a)B z6UqSvXm?xCAr*)!O}m~2C8_CTj>lj#ynM8+Z@@uA%jz@0E zu+r_#Q`L^^y8ppZo+IakV};bbyYVd9~gOCgh|A{ zsoc|&M%j~x|Mq|c>V5_yO<4x))@vTtc~yqta{4?94JuUgPN-8=$=MC`w)?<^mDSaL zQk-Aq2NtUVSj@_U1+YdZ=jWs~DmHdn&`MnLp^6e@T!UD}!TP#m+i`S`%|~RP)t*!M z7zpFi+ft|%ZtO8(>C@a=G|w@fl9WncBrbJeFYFahF7)4XO59C*-e4J>UeK+JnM$ev z0Ij;@6DjWJR>+|riDU{@fZW0K+=9fq^t`dm;>NQ)^UWR5+V3=n6|SKO2fs@`u*W^# zP$i1|ka+%U{J>P4s1Eo+Un~w0T*jYy$W7L`hZw4aJdE`e$C~qv>ANYP#5l3;t*2kU zKIir{1oZtY7T?7S51<`LT2;!z;*jXO(81h1piWK*B@~rG^R4r=pL_BIIK?qrfqfZ4 zW1DJNnLgguZYIPC{y)30uixp)+OP4{=}U}>cyLsM)e%Zb5IOG^5nDL9ydFk!eSXS8 zN(a0?Wl9_XXuDpUnWszjEIN`Rj}Rt^Py=kdR3>FSySFopV8esHampF|m|rSdg~kw2 z7%tR6bFhdfojw!1jFeapR|pUYo2pdl?HOabH5jRJU;lVASp306_zS`X5xcO0jA!2` z)97dwZ`{F!$}3PH?EC5xKPH)nF9+o0>zRO0C=)q^+>(v$RR&OAMWG7`aO)y2Gewo` zrq4`-r4fG$PWXc(tC?fk;fuwd4w7&TkJZl4D2%lvLT_aJd=BBDbB5Z@$e_3Q7m39# zfmp`mF?ei-S?pCU|B{ruX0Yv0HQdO3GMR*WFJRRC=cwxZnQ?RSCM@wZ{hi>L$FofV z`2|A~n9C}mL>TzW*P~F2Z@xxGMg?vpvWr|3{ZxZ^SqCs6V?D_>{2r~&u~12dHv%?g zEIc4rqW8`I9;8<4H2uKYLHcsqzrQVb{d`qU=h-W_ zrF#am$5D7uApU)cAPIh;|Ivk7lzLF>d1z%w(F-leR8Gu*JC{9s7Me;>Y{3II0Sw9F*HHmVq@QwH&u;>dk)QUQE9x?AkZMU9e zbm_=;vB4|Zu#2DcizrWC=Mgv6S6c4f(x`I@$}P<)%+Mcir~|8>_OKN*@?G`>_mv!%PolN1U3+)nu;Y0O|V;NJ@2;c7{7)hE{Z2IvVqO8rZYdzVVLN^#In8Yegevo2|+SC2PvcG(rfjk|flXZm$KEL1gRe>pLl`CwLfl?7?PK!R$bO5y$Mj;J?&Ony!4% zGx1PM7D-1MBvE|7tIoIpIGcUY8!8MW-^twgTM8KHpz!z4m3mutR8NAF_BQ=)6p=T? zC{IsMZ~X}(zMDp^3cJGm%Sn^+rMZrUmRl*Q{sJxeHiy}C)iP@RhciG|BU8{HjC-{n zj-8Jg8q5uG^`!dh<1K&J7Bq&P;05?f(;g5Da)b+|jlUs~vQ8EMRyd55@U!rNP+C`z zwIzl#lBSxK!N^z6F0s=8rfLli(y5a1V9T^QW-Tl#Q*;Fj}sG9uL| zXVy7st~?h@BmGMPc6w;V+*x0=z$9T~;&3+)(!gvNzIbQ4rQ2rr1PsmA`sU&BRNQJ95|5ILnqVLS zQ0}x}{VMwntvJ=VtZ!t*2|_tR#98KZ*LuH_OV)P*?Ljavd>$O8HW&VH6P*R@Di=IP zupB8+AZnwMr}MR@hb9Q_l!EIcX-@5NK}qaq=6vmI3lyFa~cybc9+c z*=2kBs0AqvQ>#^X48@O{nBkrN1rf+O>x)~eL19K?YuZ_O<=lbPj8eL^w(FgJ2Ok=)@D2s--JE==k8!!$usWSkuzykuQ4htHK$Hm zB~Kr|pl*vMsGx%`nT!YKz(te0xm3Wb?4K7D)p_~s6HX|k&-mtL47e~-7+S9j!ctT@wGdevG3ywEQ;w@&&=F8YTA!N?hOrpv&0ttI0Mc_AL|%-$~qX-XIt`giOQWlNy|!euPpJ z9J~zbtmZRy20qlhEMId*@&oIUm`gWR2)21NUB*Bu#X;yFxW~g{ebM2eh-gLrp7RAJ+jZb? zzX~GOS~>2u29e)c7b81?AJb<|^NuD%1-|lqTvKS5t^qtLZr{qJ5W5Kf zi;u%iO>fm|$)rx&fFdne0$X$PWD0jp3C;9+ltrqjXWSkbDaHp_LLdMF$w^nuLcL=& z?LpgM!#byEUcV*1tEF2ga%r>vA@8{adRgYm$RQ=@luzKN~S2FB-12D?x;hD z@xHLIJeD9hPD?y43en8v|003c$VDx%!{R~m)ts_|d-JbovAl}F23Ac;M z^@YWoy%Nf>kum?HPV0WR;!Em;rIF(5eU|qzeRMrTR}e3x1>;aKV8@L)%Ve+OkHsby z*#SiNp|M_-Wh{v@runSmoFZ>E4c)a`x(gfM7*kU-fD(TeM{d1ben!K5El3-J0_c|S zSy#F2QnW4?Yql3Ssc%?V@kwRap2-H!R~jG?07 z#B>T6*eviY8luXcxc+6a`Kxu7T3G=;u*5&$e+kc~0Y2&X^WpVerB#=b9MsJ~H_tS? zFtyU{@HLfSfkA;8iORh%H0n>ny&^n$g5}$uGxjn8V>KOOf|9-AfFEW4be}W6*(+bb zvJqy$fBE4UQQ40_f^0K<&jEl?12E7&hIDhKR=2lHDnq}rpAkXid3rgLbM|C$ZI*>l zYnTL!F784C(hS4iJT^o77`M?!^y1(H%qU`P&Cs7o5 zieJSD%4hGhax1IaiX%Jo8*o#dEg_%@1Me`l#5hr)^0c1ryVT25^IJVm?X5$vTOHHQwX&VYpoG6FYBZjtA~-3 zKjXDJ0q9;@WL_NW)*G_?_oG?A6?9rfo2V7sro85!tr(!x@bluZuYJQ5kJhjcKR74{ zXEzljP!dfj=|6Ht_ZW}8;HFhzRC{t&;AE8x_qSKY@{p{pngQGwjn=i>t3}l5xNw zF&>ifdRpALzV@naw?QlnUijQV4JoryT6Z?CtGqk|fRSi|#gYExjRixhikeuHQVm#& zb!M?g1g@1G0MIYn0cfB&;3Of)Jm%UtPw{9ut)QP0W41B)1qcFyjeO62)qfv0)O&NJ zGxb< zvP3W23-0?Uh!VZ@#SPsd0JPZ0am%VimnzsC(K~!VAIXX0N(gu_9TE!z0RFL0Z+61? z&lXpKiFiy)a-}%smH?ae4+JK*cIFxMh7-v^VhgGogU_#6S!QhE1Jz;M$It=XBr}PD z%<;Cc)Ez-ImKf+2-%fJD872Z1(cPU(ZxWm0bJEujd!85DA(!6(XJsf{*hYc$&(8uG z2=?B*5q%Xw;vs0>O(ttbM{byyVtqxv=T4qC-8JPQG}*45%35!X`l4P_6mS7!RDi!< z^^0((w-Ee~MxZ2>=p13=irto( zLY~D7j%W9LAc=zPGDaK*F!Wdhr%)PLt#wiWZ z0^fL@MIr0P`1Th@cl@Al_e)TIP$JHzxj2ND^=BAvBX?LO!#IA3*@^%_U)TYIv3p`t zjSphYE^@!+t|F8sqbz;ZNKDVqW2%C={#;*>R?#~2Uy;NbDw6Amd;`Cy0E%Y3_X z5veFQr6QLwk&a1)z$NxKxLLeThQg#x0eY@A_Yz=4$JJw}!r;npD=wt*(i6n5-yC7- zAgbOC7Dc+t09H=8hMF~l72?YD(b;*yiMCM@@qrFtN;my(M-Iw>-UpT$?yppVA_)4L zC)#Co9K;;5rHF+r@$U)v^xCICiRgQ2(RwbncsVYs{{3n(rf`oQ=s#-$RCV#;{#kn9 zuOfvXF{Z_n#l>KJj~QP=00dZvZ4wMD18cp+l$5BkMC2iua=5<|hsjX2xC`cKSewt& z^-h8LvdY0H=qbpVrs8z=B)`w5oVg%yi3V-AFMj)NrA~bGezC9b_wPZ(+2aVU%m&+a zpxW9(snZ%7n0MMyBesr>-UC-G)GlVrGtRZs-ZnAXVYLQ0cGDdPSr7Wd=w4y2;5%bP z{v5*o+y#j~fz!0h6cXDbz2>CUf%jJ|%BEAN9ne2mWs(Z+FlY7RxgAN-D;2tfCqeO- zA8<3T^wr4#Eq1|jwe*!4%}o&)#{jZ9XFN=bN)PcX)o)yV=F@~J3g5qYQ2GT}ty97r z9UmV{e4bmxbC8iF>j<8{CkaJ1?O?Hw4rk|D2SK<;SXh(b4*|)BAEedvi19pBBHXus z}NZ3?Z;o-c;|*Kj|Y-6*rcISCCRuWt61 zWUge^q)MCEz_T~!$P%wNV7OXzC5g&!@1*>8+0L_|in*u-r=~Ig ztnu{|WVA|iV5D)~LZy;Svoq1tUy^pC3gGEeTw0x$jxRITOh$=ssb$hBs`;7>%O^7M zHr-og;(-5^Ad+&dSq6bZ#qD<6dM8ZVpN}cpbX4}_6{Hu~u${DY|KbaD#YlK8Fb{RcAjcFOK%FTjvtP3&k)iF#O4O@B$TRjmG|7h!>Lx#W*MUEW$ky}N(bLPhM`!YPs z_9?zW(WUDEb{juE>CCpv%plXpAzvb9E)Uqx1(Q;>8fVByY862y*lY4C6gTz+!GTu9 z!?SgDhPo_wk&O-?ZHLm>G7+uMEmR_ZzD#lhLatjp|I)*LUrJXU`*YloRWJoPT||>Y zCkE?(xg?HZ=1B56i8?to>HRB&L|0W&V2xgH7S+0Ca{3)2AR^25%4co- z=@!-h7x+_x{P@{+5w+PtfckZYhIlK(+>N<*ey%)4j-kA(Ot8uZLzeR!F*thqhqxEr zcDsem{uj!m{7}Sj%385D?@fuyIAaYISO7H^KY}FU3_0zQ%)f=C4qflV3vz91pXWyz zU+b`2(Z;?o*#A)+5R4Q3tDaLOy~GM@aDr03%B0;wBlQM}vMsxcNy>nyw9-{FghVI* zjaxzz^g1AK)Rq*R9aN-Nz8H#iJ>H`PfstMZ|4zBKt>q+QAP4j!j#{5`&fiWnp9K^T zvPJFliJh|{86>b^S)O*lzQUFnDLkxjk8eGGwp;kc6LokZ4vZGbIr9h~v{T@msG<*g zI$2y}a~P6rx~u;pH30&Ur;WcOO3+WqLn$jsne{_EM0em}wSE%+N5;r6_IzKJ**e2H zhucF7ex)+e`Q2w#1({>Ng>t|!0wC>*@*+NkNetx}vsz-ehOGeZ2h zOHaHKE9$R8ldx&YUI7mcm?7oo9;n6!fcrKf01tQDp^O?+E(-afigSY+VOs_J^_dZh z3d<3!ixwrJK*dTsks=4-i(sf|%&LeL@jxQSSV!RjC-1*<+hX96 z?-hr5mAsP{!ni-hxbj9Ua|k4}PardNA_`Of6%*WxzL=}D+`f7o(!Egz2ILv?40)jF zY11j(5JB_PD+o}w)?zZ0SDMVB!qT@FVhCat!j{ue=o$%d6}t~Lvq52q@#Wyr{G6_; zJko6PwqO5e;;9q%lma-iSqKxqVqx&p#i-UdtkRntOG2@+f zknS3l_IHT=?s>axr^AddHL!$an618%Ar=t^3JlRHQH@)>yiL1XZP4@@XH|v)Kti8k z8`Z^xHB4m6oC#LgJ31)XHcorAk=#~`^{>xYIenVyrAm-aF=1eyjU2QX-GfwIyJ^g` zC8e&5M@p!z;_TYa+cmVz1)0P#vYSNw84ujT(Q>wK;5dI))k12PQkOfr_rxMY)*=;A zSiWxy`B2K2Hi$$y^i&Tag z8Ht&YjSh3UeoT9ri+jq!*TuBw9*8Z>Ay37~$N9aXpPz*%=(80BA87pbT~;u*wyl9- zxD--AB78uc1tLtr({A|1CGMiKHFnF1?D_lsnZ-axV7wbpR0K{004TJlFBskKH~aAz zUL~B8zE$11AbsG<&o(^XE-O(rPQmxNU}@Dk?hK)O=RZ*TIxIrGk{`>{(&zgW_v|8L z?S?`lLAFvqiE8mlvM_9`>DoW<22I1QDa+MnEzmph@j?WP8o3own3(_5DPm=he7|&b$0}3;@vu!GARaV@C~kP zSrS(plS#52iKmzF#PAV-q$1YD5Pyn8RNo(6j8NJ|KHE2;*#-NBLrOYP6cXkR7@oru zJ4h-S_-G+WGql|T@`qRtZV_Y-!L4m-p+O!CBH&}hIU#yc+A4q5XMq9K6r1Sp7Kd)g zK$OM}%4bqoCI3wWJlw@p7*9&$Dvr?MVwUR5QYBOD5D3I=49x=m^k*3m;$=>#cH+@v?qsgZIv6x7Dw ztq=@v0Nfpjiep<-oRV;z^v5~e788ZUh!cm>Jrl@9eodv5Dl~hP99{9ysOjRSrnCOX z#yd}P(-0(RAXPEgL;(fMlCzUa>7ng zEyYJ80q6{DIyqllT;yFy#;7uY#Bka174c7=5rXtE;E%s5`H(T3u0j4rU*E*VGLYlR zQo+sT)6D^|g6mxUuc$jx?B?tlKY@VkQ$u*axcbSYW$m2FW$qW?qX@`bLh!);-03e;q z{Y${v2%jf?3du(jX6&4FF891!On)4d&;64fM&yB_bj`X&(loz9rEi1(6o~-ONZaxb=1mQH^mxCGMU_r`NB;AW{1Cvk7}ik>=+nWo{zNM zh2ZVS{{cKG?KpL5NUaT1EX%US85+NZ;EMr8R}1B+bgz9&!Zg25;>QTR-oOATr=)Y) z08N3h!CEY})S_DYIu=YEH%hFN z1~SOZ&1Y_~dcuk`K%7!V+(M&yd)&$iU15TRM8q&nb2Eedpor`YU>6cQqO~#c50dKK zOfK_i3)>tu)@cJ7WZNlICamb_JeI=K!sMoC5~~5c4qz#Or6AscNCe=f5KIO!8Nfu; zqH20}w~KXwRfJp}p?h&F1*69j>kY(% zL^Qe^l=?h04uGJ0wENRS2*IO5-lXy!H!WPeaOr4wXf#=GAOVPoBu#TiO&<9F?cKpj zTR|Ab@&B1dEu;&P?j(>vLAtF<_k9EP4SWFKqiZ)Ve1T@wx6mwX>8@g1Q}F7>LKg+$ z)|t=61rf1L(q@ty`F{HgvpI0@%$@Urw#dI_Iu!=JAl`-R>+U2bH_ZwU$cU=D_St(geQdCkjr6?&WDJG>DER;sPh#?fsX7hZUEiA6$0m-B)j%)v7 z=nk)2#h3o+kfR@5Zjb81RNWZ-OW9j3I%!?S1CoiEB~{axMxsBrFS;r4fMk^pCi9HT z#RHPjliv$(88jXD4#8*U=Jqo#$&JVZlF@N-d@0kgv)L2awx3PZiw7jLlkeZUV4M8C z5BRIZo0ERuU$1J$w|OU|)oLw0Z8Ubwcn!P)E0I1J96YgNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3UGPRo_qPU*`(NZdcWJ98M$9!`1Il)i+~gwFL;;R6`d~>lZcL9}0v-V#%Je%_H8BDGf%(9D zfM!S=fYE@Y;U>sB9$1rt{R(huLfa`B`xlqSc{nF~_17fG8iT#8t;+q4F<9H3tVe5r zbuTc#!nXHRF@Ehb;1_@qz;fVV2F!iHQlNh-Zi28z;Fk%&dx8G||4LEnCxL!t3|?p8 zgF@N>*|1?Y&`t4D&a!^}^BRlv2?fT`+! z%yk+}SVKf)RPZIhp9rVs5w%!y9PZJWtrD0IJ!5z}U>kv8_jzIs<>zfSTnhZeD{lrm zmC?V7%?3Aa@3{86fOgo-w9nN&PlkO#EzJh(McP4VduJYt4A{fHzH9=dAR=8cyAy09 zbbmQ-MjEL(`4_li1NH_*d3FM~x?&q(k%+X4^3^fT0DkZCJ%Rh0sQ=Tj@dB8ms&A{A zd3U?I9>k1y&NLc0#^vu))z@nUtg6d_XI$}9z`z190S>v$=BZQj8q)1veq;jA4}q;N z-yc{jBE1rL>x$fXO~hP40E2+L-DzPiBqFUb#b2u+3;auTy0`^^Ad3WOI?URB%KXke_%Ga4~jr1TYL(CnBHDsed&iRowubhn+43KHyW*TV*46gnFeb z9>DC|Bx>r}*FOSo3SDYhz?|Dr;kBmjUOv+fR8?=qu15yf5|0P|Dk2}Rao=iAs=5sL zjw?1&J0I{E?5ZfhB30d-#Hgw(f%UFlRnl6}aEoS9MLQIv?|*fIZ9yM5JStuvdHe(?#Ujw1m0tz*c}6Rmv_!{&Ve` z$W;Z2NIT4*4e(k4c-j>jfYWji*aqnBid)>qCNrt(^S~DgH$tDqPI2m?A8K`7_{oBGRK0*;&Z>^aERWa8ADNL0U+S=PMeP+w9~345t)iD z^I&mFk5viz7cbu@k*m%~Ro})g)4%MhrisXeiTo&zJ4EE9Y5_&$ax4HC zY_*yV?nF)=dE zu{JN}F$h9wo*`p6Rsz;Xu)_nzX0MXZmC7S2#69<*?Yop2ZGxFNzmAO4lp>y(Mi)~Y z$+;DnR6?;@Ii6^g=-^@#SUE4cA z&BHcPyCS>?%22TG*gfK)?H=GR4}Vhx`w_ASgx0O{MwIg~<;CK+f{W!_sPbaD!#oC? z1=Wpt>Nr-TydK7t6bFbZc51p93#$FDdyyAnW@I!3ekX8dEOv^}_P~$G{)Zz$x(Dc8 z<`3+Mg}SHV9t7?zXAnOG%G-eXV&jx|wkKywHF6@jw|Kat^HyGm(~Dv=B1_tXc}Wh7 zCJ&N0@I-R|%P<1F?l;6Knt#FhUF?)@8E~(v{xcOYSxy1F2YU%Hfbd}BMTdWjsyWdP)7sBGH8?p(}ako9KYX(!NC_${+66f zCL^323pYiGbgW{=R3}SA9sZ^M67CqjK_%js%5CkR;me zO?bAIxNg3Ro($a-cu31+pwdB9n{8&kSIhHe>aHBjVXA_CRI@ z^iBXW91h1XCKo*~ZXPqvsnh6OjES;@0=+mMNtWPHly!FDxdig|gWCPa(Ea&26soIrWMt?>>7bAgsH$4Y)5&Bq8mV)C%YB7Y+Jw5j^+Hk8>AUFv z&`yo)gA$EKn@UpK+S;xY$oZXByDL@Ihu$X-O`7@r?DzRA6X`K2l^#WNxC<>WFJptl zO&yXgxs)7>#kLY#ED||;DijJqRXu2EXxy03=c9(D--^EXGrJ@OqsiBFdd$}K z%S6%_$lmt!JU(1H|HXST8NZXhS$l1}(mg$J6&O${e1)u?zBm5_@-L-Nh&J@-00000 LNkvXXu0mjfve9f* literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_connect.png b/client/icons/portgroup_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..024138eb33b9124af6db8149747adbb41c1b8cfc GIT binary patch literal 748 zcmVzR>QH2KN`fNj zBHaWZEgiB#>49kYe(borqx+hj`JS_1d)R}7LjHa+tu+qg(TC+eO4&q(D;ZL8C8o8; zLEfZxiIlQ~iE1b1qBZ2=VhsA0V`*@y@M|Sc2@Wtadv3g0hOc*O(ADVFjPTWAYz(JXS z8MBaa%aCO{h8lvp*ONKIh5Bg|-DNo^;jg=q=uDZ@vodOJXfu;GK`ze`H-VrWK!nUg zje)ufRUJ&ouB2nYB2_pI=gji&GcuBL=+DM-wBZ)fbc7&a9AP1Ztk5)S4Ah03bqXOt zxx}VdK|CIVljyc=oqO1G{;Qew7T~%?tSw{^mi$E(2^Td6>M8+m4H!qrBtj~%w(Y~Q zVrXkVOEN#2&@(U(cX3H&S97B(;-}{)?<>?0)RjVZqrJ&ONChgCgE9%PC}CSBp!+d1 z`bkAqacXYz!94aLsJZ!K=3b*?T#ge1nL>bsZNf4&8mt(EkXYZ?MK0YwH2ZOI9{(VN z&%WPH*v6AY+yG?)mZ`CxE@5ZK2lW}4Pr-ctMRE2V`yf8$PurT4&^p3q*2kt>M8OM& zl@MbQ=U&8BS_$dSjo(q&2Pyd!8`}{~Xr#A_DDL|G({Ha$;Xe@?&|@q4QbvV}D#ixB ey}zEqA^Zgf(1+rQ>#k7%0000IB7 zSr<&xRLO(9u(h={_FdAy0EUK!3MrvO*Y&f3KmiO&g5y9$Q%*Rnqqp}J)W0Ps5{YA+ zTvAd}77B$hJ~0JmcN`av>kyC&o4^difI2!lYS^~zClf(Ane0=k)bElpKc6BX2ZxUw z7vEG)E-$Y@I=vv+U4C3v=?dc);zUun5HFrTLsj)o!Os7L0!HQJ=8iapNsuI(y-9es z%;F+$U)e1f2jlO+YD-U^_7t#GX63+eQ88p$hD0W3jn@p|Iv!*7_8PHvvwI-30(vI^ z8H%E;Gdb&d@a8e&oHmZmbX1fj6qwoeNU{V)RrBn^a|z_V&UuWTpW3mMv4jc%z!Pr> zm%xmXO$DNUZ%CM4u*7P0pc^%V?Wpaag{2o`$?Tx6NFIQkt&?r+^PlIUHWP#Y%SY5* zYC<4Vg_YqxjJ$n~vQ*du@cC5Sy1YZQ$22W0FB?L#-|wR`Tr9LUW80ZV1jk~)n;R%7 z)Umaq5|d*Is8rXTSggM;cTmU|X_^+{?j(~*gVY6Tf6O4bIRcz$%BxaaN}(A)vFM9Y|u11$~II*CeXV}4Kt5h{aWbSmSRg)zoxZBrQl+iPQ*@N>AMLYl{sL3^s-3vtl?VU;002ovPDHLk FV1kc^U#9>7 literal 0 HcmV?d00001 diff --git a/client/icons/portgroup_disconnect.png b/client/icons/portgroup_disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..b335cb11c4d1a397b307883adcfe1e00c4cf8e6a GIT binary patch literal 796 zcmV+%1LOROP)h5&w{Y-QlBkdy7eSyz8|k(w=syt3MbOGZFmTy2f|dnI3kj6Sz)H!` z%1hM3FiovS8#Qs98Rxs5^PSs#9S4da6*};44(Iv3@B5rb3BwQ$Is?0$0ilY#Q^EELL=6SfS*Ly93GR<|lPYvfPXoM^)HN1)!-B@N5Zi(e|Ge`rl{XLurIiz-D}wH%zBB+uQIj;+Mu^GVlA1NAvn$4NnL{6}C{AT#~>o>#S z&z~7SfBN?S|KESVKw$uM1yKCYN7a}+;#ge(RJ8Ng=jT5^K70A|)5|wMSw;OAxH)7P z1!Y6n|NmiT|M&CHQe@3w0CE8?{ONMb|9h+S{@-4rG69zwtkDPp4>stWXXjRA`1|Xd zgi7@7mn5Zw`)jqX0{tr@8L*j=fe^svtUD{z&GC5+8KcAkIbh&369D%X=xO)W1B(Cv N002ovPDHLkV1h;6wt@@v-Jo56-xpcZCLFvFo+sh%yoi{VZ?9B7cjqH@ z8!|K0K+I4z)ErSm)DSg96%|L#evN4{_Y+flvN@i^7vC@LifQSMZsr<)pJA<0#?&w| zOa&KZT_b+%+Dp|_hzX*?r~AGp1Wm_0Rk|^m+?;%ybY_6erk!{YJP6vXQ(u{gS1-+DVsvCiz+&=4Z_!gK zprJ`^=?3>+I>|eGM~G4xHY`4{oCq*90 zHfq~Hqng;lg=?$CiB$~Pv85Boz}<0ozC3%&%PVDHJU8YKX5RO?@Ai3R;RkPKA6bUws5yfGJ=?vs`Qc_KTWo0goouiTL-$h^=J)M zL(O?@u!DuWRi0($mT-4AOn)X1>|Jb)Dfc3=%9wAxt9|F z+d&sV7i|Rig}f4o)2%U3C;eEDoiEh?94d(rV57VIF#8VqzW$HrDC|#U`x@QDbgi zVl)t9GGz&YY#D?gc%>hISA+_EBpnXt#pnC`p6@xw0$8TCbULjhlgVx(kuc)%xbgqq zR5+DNDFRN0!y)7Gm}oT0i39}h4h928qY?Rho^UvPGJ#kuW|-Amtrn`Pmd&+bFo@sp z$LI4IQw7BG?|#2ewOS<<3VjL$0=lMY^m;wqZujv5kx1l%Sl;V&Iy4#$ip3&@LV2!7vhhN=PCz%^9v24`qb(+m4W?!q-&~=?ssf5GfnAmJKV;3bvpDm0(NhahZ=&^sqo6Odj6>)Dq_3p~4~ zvb`d3Mydwjt&Df^hVmLtI2x=U&h9(JVYX-!y~z3zi;1>=LY;o(bL$(Yf$lf)dMf0-u^0HrpTG Wk@)HE*94aU00004HU6=o70{GE1?O|XyFbE6*6TqM+SpA<0`0%^BR|UWRofmqx&W?zi zrw2u5_Pi(#9R{2 z(GaRElJdruSs2^MyJlwMeY-ecki)*r-#wXGf6QILHXsR{1_nG)6<|?dzu7X6-K%)8UStstZSg@8U|izTH`%Pl`y0S^iqG){d1s2hX` zP|iC{PgkkbY|s@gVU51NR(g^h*wRGd0Fu7Wc1l%+pSyZvYc|HB$xVCKK1pBV3ewdz z9id>G?g<1hBcS)9=-o92XdYFq$3)t+5wOj|n=vB-h^%YK@STQiuAN17zkgzy63vcir!BccYXSc93Od&Evr98 zxE1^vqq8VNI$lYXROsjop0Fs-#%VPYh?+)c+~n5JhuHJwD0}usxO%;gQww6)NzNPv zR|T0EuJWg+Q*2yuQ)e=^w)5WGAK{IW{Tw^@2NK;80&OTE>k4q11Z-*JNP%)CN+?G9 zTWD|VMwrl3gj-#%+b(g;0JU^4xs=Phhf}o9-#{X=I&#~NyGiuM zIezA4l8NO+Vg`w2vGDSqwXML4W&y`UNDJ2$j1Sf^R41AWQnxZ}zFAVNm#En_G#pO7 zE;;(Q5JK9-`wB!t38sb&(y0ogqmvAc^s{_JoEVTeDlG_(ZVJ-Y}uN8c<&P% zfZa_3rc=R|b);)j$~ia!a+v}hS7s=iB`l|gp$pzRP$M(iOHhw+^34)mtMcTtB?>8n zHP=RHiPtb?-Kxq|AzvG-6bjRrZjQEC081;>hG*W*0q2_p>Y|J-D>$mk2Peu@=Bs4V zIof+GSaKd+H#zz0JTo7}LFp7L<81#zmDrV4#>b`@8!EG5dzhUM*_6d__PbBr`^GcR z{%B5~I*Wo=<8KsNss0}2sW0ER-kNB;bEZA*5vEju0>J%cIwHOtX~(&lPy&X9*4;%Cml_!IoPhC@0T{$2)0{ z#wnM}+`bE6fd7!dztFHKI?X}3ZbQH^B-`(7bOC^4ufXqqn&!q`?SzZ~($Yxu(fGVn zDtQ7Wa&tCIWN879kE0YdVRP&KZ6w!K8W68#oI2w2{kv0q>y}Br;nj;jFJqb}*=)v> zC^IrpUwq$K`c8QHE+{O=p;WK)z>i}b{8fn~FO@M2V?o3#P)d4mi#3}=eDw!C?0BZ~nbN1*rR@SX$sx7bG;Q1CG9Dm2)$q z2#oolVc;ZC@`0ugls<6D1U$2nH`Cu{XXJ8V>+G<|deHjt2}_VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/client/icons/sound_none.png b/client/icons/sound_none.png new file mode 100644 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/client/icons/stream_add.png b/client/icons/stream_add.png new file mode 100644 index 0000000000000000000000000000000000000000..04f22badca1ff91737468581b5a24295bd0814fa GIT binary patch literal 814 zcmV+}1JV46P)2z8@H#eso9vK+4F-h&nJZ&{G7+WHR>s{e4_qTwrf+4t zo}Sk7`8;yD9400vG!kEtGboCJ$;nB0ydu)MsC zrluyXzP`StsD;JFMHe4lUth<{$_gY&LO2{oe}6wa0=0$N*;!HDc=xP zGnYF%JCJ1=$z&2vr!(Ey*l1{NZ8iA){`CC(ya4fcpU-#ccDs+;+S+6tiPf{SGhvr2 zQ;--M8bW(}yWzS@cXzjeG60KH`i!KUM1jQ>oB#e%Zg6L7WWGu8f3f0;{|@fi^gbbu zMx!mm!^7J4_O=l7bf|2?RSyLh4Jr*XM+s*`=%WZM^9Z{oocaIl!k@|JwX(9!VVt1xutof=Wt6k zLhSxrQ|#b+5}?d#wU#zFH+9wmSwoOxbVANu8-ICvC9OZP{@IRIMx}06RoYSO|-wdx|%W=3>I87B3X;u?cVu^ z;VyxF2;9b|J!~>#$>nkxsI*$GOl#-o=X+S&e!t&$gfL`&i~u zsRYGh5fnus!hPJgpcSsMv2k#EdU}ko0zHtGOC%EHg^{A!Y!*3=Bk!t;!{MNHk@g~y z2m}IwDw1%=pitc_W_G{D{G&il8C{Xwocj8wwNOO=jZ1qtXAtNDN$f_3b9yBRcuLaf+-$^4woBr_S;YlEx^y^MLD~>^I9dCo11%s zD&sf-J3c;EC!nGep|SJt`oQ_@73jlX0pi~POgA7c*kE&E`L}uxFarexVtT!vE)|5s z;XF=Y=;`TUL;&dnsI%Aso($J=5#CyXS6Exkg4gTyWipxP7|vlsL&N?0`ufe@-d?(u zkQ{#`KYZH9i_y_eG49s=LNp*;OMt7gCr{Rxm*rXsT4${yX7A% zfy$qf9&)?}vKa=y;!H;A_w0Y4^U%=H0D}AJh#6!4Va@lZeCFUKFEg9WSL2BK@OYsz Z`4?5=&N;mrg`ofd002ovPDHLkV1fozjIRIy literal 0 HcmV?d00001 diff --git a/client/icons/stream_edit.png b/client/icons/stream_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..47b75a456a4bb1487dac02c60b7e2e9cb5e210a6 GIT binary patch literal 865 zcmV-n1D^beP)GR@vSHz5C(pPxHny(nM!6aSI^7R#-{J~?A^0H*YCdW>wX><0M=1!YHEsWHk&65 z2EzpTxW}DK*f^ce)R~!?WFk%?;`Pu5C{Y@ z9*YwPJjH96dcf=xJG z*kl}##L?N=83%;ar!Pg^cVqOf0lN#g5Vlp~vzQCln=Feu@%+Ain zAxsXvy}hQ%p)0kKIRWUX89RYeM1w`x^47xBl|kR;=6}n}%d;PbNXFDkg2d$HB$&Tm z{utC0|DU)7(XWO0;TB^4`9-wVaC#H&fkYyy8yXslc|4xD*f81w?^q4!T|J^pT>J`N z!zOX^g^2B+#!yvN6)P_<|3AiofdL_tJahBjw(;Om)xxQMf{?WUJ4;0fJMO^QaUmuX zzldkm(9nR~++1P8J!ouf?5?h^rbixT09(uOy~>BS_9Toi+4ykpEG-e7Pv>wrR8CF~ z&1SQ^k9 +*/ + +#include "mainwindow.h" +#include "../common/ostprotolib.h" +#include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" +#include "settings.h" + +#include +#include +#include + +#include + +extern const char* version; +extern const char* revision; +extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; + +QSettings *appSettings; +QMainWindow *mainWindow; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + int exitCode; + +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + app.setProperty("version", version); + app.setProperty("revision", revision); + + OstProtocolManager = new ProtocolManager(); + OstProtocolWidgetFactory = new ProtocolWidgetFactory(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + mainWindow = new MainWindow; + mainWindow->show(); + exitCode = app.exec(); + + delete mainWindow; + delete appSettings; + delete OstProtocolManager; + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp new file mode 100644 index 0000000..f69a856 --- /dev/null +++ b/client/mainwindow.cpp @@ -0,0 +1,153 @@ +/* +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 +*/ + +#include "mainwindow.h" + +#if 0 +#include "dbgthread.h" +#endif + +#include "portgrouplist.h" +#include "portstatswindow.h" +#include "portswindow.h" +#include "preferences.h" +#include "settings.h" +#include "ui_about.h" +#include "updater.h" + +#include +#include + +extern const char* version; +extern const char* revision; + +PortGroupList *pgl; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow (parent) +{ + QString serverApp = QCoreApplication::applicationDirPath(); + Updater *updater = new Updater(); + +#ifdef Q_OS_MAC + // applicationDirPath() does not return bundle, but executable inside bundle + serverApp.replace("Ostinato.app", "drone.app"); +#endif + +#ifdef Q_OS_WIN32 + serverApp.append("/drone.exe"); +#else + serverApp.append("/drone"); +#endif + + localServer_ = new QProcess(this); + localServer_->setProcessChannelMode(QProcess::ForwardedChannels); + localServer_->start(serverApp, QStringList()); + + pgl = new PortGroupList; + + portsWindow = new PortsWindow(pgl, this); + statsWindow = new PortStatsWindow(pgl, this); + portsDock = new QDockWidget(tr("Ports and Streams"), this); + portsDock->setObjectName("portsDock"); + portsDock->setFeatures( + portsDock->features() & ~QDockWidget::DockWidgetClosable); + statsDock = new QDockWidget(tr("Statistics"), this); + statsDock->setObjectName("statsDock"); + statsDock->setFeatures( + statsDock->features() & ~QDockWidget::DockWidgetClosable); + + setupUi(this); + + menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions()); + + statsDock->setWidget(statsWindow); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + portsDock->setWidget(portsWindow); + addDockWidget(Qt::TopDockWidgetArea, portsDock); + + QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); + if (!geom.isNull()) + setGeometry(geom); + QByteArray layout = appSettings->value(kApplicationWindowLayout) + .toByteArray(); + if (layout.size()) + restoreState(layout, 0); + + connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); + connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + + connect(updater, SIGNAL(newVersionAvailable(QString)), + this, SLOT(onNewVersion(QString))); + updater->checkForNewVersion(); +#if 0 + { + DbgThread *dbg = new DbgThread(pgl); + dbg->start(); + } +#endif +} + +MainWindow::~MainWindow() +{ +#ifdef Q_OS_WIN32 + //! \todo - find a way to terminate cleanly + localServer_->kill(); +#else + localServer_->terminate(); +#endif + + delete pgl; + + QByteArray layout = saveState(0); + appSettings->setValue(kApplicationWindowLayout, layout); + appSettings->setValue(kApplicationWindowGeometryKey, geometry()); + + localServer_->waitForFinished(); + delete localServer_; +} + +void MainWindow::on_actionPreferences_triggered() +{ + Preferences *preferences = new Preferences(); + + preferences->exec(); + + delete preferences; +} + +void MainWindow::on_actionHelpAbout_triggered() +{ + QDialog *aboutDialog = new QDialog; + + Ui::About about; + about.setupUi(aboutDialog); + about.versionLabel->setText( + QString("Version: %1 Revision: %2").arg(version).arg(revision)); + + aboutDialog->exec(); + + delete aboutDialog; +} + +void MainWindow::onNewVersion(QString newVersion) +{ + statusBar()->showMessage(QString("New Ostinato version %1 available. " + "Visit http://ostinato.org to download").arg(newVersion)); +} diff --git a/client/mainwindow.h b/client/mainwindow.h new file mode 100644 index 0000000..2b68ca5 --- /dev/null +++ b/client/mainwindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H + +#include "ui_mainwindow.h" +#include + +class PortsWindow; +class PortStatsWindow; + +class QDockWidget; +class QProcess; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +private: + QProcess *localServer_; + PortsWindow *portsWindow; + PortStatsWindow *statsWindow; + QDockWidget *portsDock; + QDockWidget *statsDock; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +public slots: + void on_actionPreferences_triggered(); + void on_actionHelpAbout_triggered(); + +private slots: + void onNewVersion(QString version); +}; + +#endif + diff --git a/client/mainwindow.ui b/client/mainwindow.ui new file mode 100644 index 0000000..333b2db --- /dev/null +++ b/client/mainwindow.ui @@ -0,0 +1,84 @@ + + MainWindow + + + + 0 + 0 + 700 + 550 + + + + Ostinato + + + :/icons/about.png + + + + + + 0 + 0 + 700 + 21 + + + + + File + + + + + + + + Help + + + + + + + + + + + :/icons/exit.png + + + E&xit + + + + + :/icons/about.png + + + &About + + + + + :/icons/preferences.png + + + Preferences + + + + + :/icons/qt.png + + + About Qt + + + + + + + + diff --git a/client/modeltest.cpp b/client/modeltest.cpp new file mode 100644 index 0000000..2598c58 --- /dev/null +++ b/client/modeltest.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include + +#include "modeltest.h" + +Q_DECLARE_METATYPE(QModelIndex) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(runAllTests())); + + // Special checks for inserting/removing + connect(model, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(layoutAboutToBeChanged())); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(rowsInserted(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(rowsRemoved(const QModelIndex &, int, int))); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + Q_ASSERT(model->index(-1, -1) == QModelIndex()); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(0, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + Q_ASSERT(model->index(rows, columns) == QModelIndex()); + Q_ASSERT(model->index(0, 0).isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->parent(topIndex) == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); + qDebug("topIndex I %llx", topIndex.internalId()); + qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); + Q_ASSERT(model->parent(childIndex) == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + QModelIndex p = model->parent(index); + //qDebug() << "child:" << index; + //qDebug() << p; + //qDebug() << parent; + Q_ASSERT(model->parent(index) == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) { + Q_ASSERT(qVariantCanConvert(variant)); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(fontVariant)); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == Qt::AlignLeft || + alignment == Qt::AlignRight || + alignment == Qt::AlignHCenter || + alignment == Qt::AlignJustify || + alignment == Qt::AlignTop || + alignment == Qt::AlignBottom || + alignment == Qt::AlignVCenter || + alignment == Qt::AlignCenter || + alignment == Qt::AlignAbsolute || + alignment == Qt::AlignLeading || + alignment == Qt::AlignTrailing); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) { + Q_ASSERT(qVariantCanConvert(colorVariant)); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + diff --git a/client/modeltest.h b/client/modeltest.h new file mode 100644 index 0000000..38b6b2b --- /dev/null +++ b/client/modeltest.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2007 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Concurrent project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff --git a/client/modeltest.pri b/client/modeltest.pri new file mode 100644 index 0000000..358a077 --- /dev/null +++ b/client/modeltest.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h diff --git a/client/ostinato.pro b/client/ostinato.pro new file mode 100644 index 0000000..456f349 --- /dev/null +++ b/client/ostinato.pro @@ -0,0 +1,95 @@ +TEMPLATE = app +CONFIG += qt ver_info +macx: TARGET = Ostinato +win32:RC_FILE = ostinato.rc +macx:ICON = icons/logo.icns +QT += network script xml +INCLUDEPATH += "../rpc/" "../common/" +win32 { + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostprotogui -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostprotogui -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -L"../common" -lostprotogui -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 +RESOURCES += ostinato.qrc +HEADERS += \ + dumpview.h \ + hexlineedit.h \ + mainwindow.h \ + packetmodel.h \ + port.h \ + portconfigdialog.h \ + portgroup.h \ + portgrouplist.h \ + portmodel.h \ + portstatsmodel.h \ + portstatsfilterdialog.h \ + portstatswindow.h \ + portswindow.h \ + preferences.h \ + settings.h \ + streamconfigdialog.h \ + streamlistdelegate.h \ + streammodel.h \ + updater.h + +FORMS += \ + about.ui \ + mainwindow.ui \ + portconfigdialog.ui \ + portstatsfilter.ui \ + portstatswindow.ui \ + portswindow.ui \ + preferences.ui \ + streamconfigdialog.ui + +SOURCES += \ + dumpview.cpp \ + stream.cpp \ + hexlineedit.cpp \ + main.cpp \ + mainwindow.cpp \ + packetmodel.cpp \ + port.cpp \ + portconfigdialog.cpp \ + portgroup.cpp \ + portgrouplist.cpp \ + portmodel.cpp \ + portstatsmodel.cpp \ + portstatsfilterdialog.cpp \ + portstatswindow.cpp \ + portswindow.cpp \ + preferences.cpp \ + streamconfigdialog.cpp \ + streamlistdelegate.cpp \ + streammodel.cpp \ + updater.cpp + + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) +include(../version.pri) + +# TODO(LOW): Test only +CONFIG(debug, debug|release):include(modeltest.pri) diff --git a/client/ostinato.qrc b/client/ostinato.qrc new file mode 100644 index 0000000..6162ac0 --- /dev/null +++ b/client/ostinato.qrc @@ -0,0 +1,38 @@ + + + icons/about.png + icons/arrow_down.png + icons/arrow_left.png + icons/arrow_right.png + icons/arrow_up.png + icons/bullet_error.png + icons/bullet_green.png + icons/bullet_orange.png + icons/bullet_red.png + icons/bullet_white.png + icons/bullet_yellow.png + icons/control_play.png + icons/control_stop.png + icons/deco_exclusive.png + icons/delete.png + icons/exit.png + icons/gaps.png + icons/logo.png + icons/magnifier.png + icons/name.png + icons/portgroup_add.png + icons/portgroup_connect.png + icons/portgroup_delete.png + icons/portgroup_disconnect.png + icons/portstats_clear.png + icons/portstats_clear_all.png + icons/portstats_filter.png + icons/preferences.png + icons/qt.png + icons/sound_mute.png + icons/sound_none.png + icons/stream_add.png + icons/stream_delete.png + icons/stream_edit.png + + diff --git a/client/ostinato.rc b/client/ostinato.rc new file mode 100644 index 0000000..41983b2 --- /dev/null +++ b/client/ostinato.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" diff --git a/client/packetmodel.cpp b/client/packetmodel.cpp new file mode 100644 index 0000000..ecb4196 --- /dev/null +++ b/client/packetmodel.cpp @@ -0,0 +1,244 @@ +/* +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 +*/ + +#include + +#include "packetmodel.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +PacketModel::PacketModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) +{ + QList currentProtocols; + + iter.toFront(); + while (iter.hasNext()) + currentProtocols.append(iter.next()); + + if (mSelectedProtocols != currentProtocols) + { + mSelectedProtocols = currentProtocols; + reset(); + } + else + { + emit layoutAboutToBeChanged(); + emit layoutChanged(); + } +} + +int PacketModel::rowCount(const QModelIndex &parent) const +{ + IndexId parentId; + + // qDebug("in %s", __FUNCTION__); + + // Parent == Invalid i.e. Invisible Root. + // ==> Children are Protocol (Top Level) Items + if (!parent.isValid()) + return mSelectedProtocols.size(); + + // Parent - Valid Item + parentId.w = parent.internalId(); + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); + case ITYP_FIELD: + return 0; + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + qWarning("%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; + + if (!hasIndex(row, col, parent)) + goto _exit; + + // Parent is Invisible Root + // Request for a Protocol Item + if (!parent.isValid()) + { + id.w = 0; + id.ws.type = ITYP_PROTOCOL; + id.ws.protocol = row; + index = createIndex(row, col, id.w); + goto _exit; + } + + // Parent is a Valid Item + parentId.w = parent.internalId(); + id.w = parentId.w; + switch(parentId.ws.type) + { + case ITYP_PROTOCOL: + id.ws.type = ITYP_FIELD; + index = createIndex(row, col, id.w); + goto _exit; + + case ITYP_FIELD: + Q_ASSERT(1 == 0); // Unreachable code + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 0); // Unreachable code + +_exit: + return index; +} + +QModelIndex PacketModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + IndexId id, parentId; + + if (!index.isValid()) + return QModelIndex(); + + id.w = index.internalId(); + parentId.w = id.w; + switch(id.ws.type) + { + case ITYP_PROTOCOL: + // return invalid index for invisible root + goto _exit; + + case ITYP_FIELD: + parentId.ws.type = ITYP_PROTOCOL; + parentIndex = createIndex(id.ws.protocol, 0, parentId.w); + goto _exit; + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + +_exit: + return parentIndex; +} + +QVariant PacketModel::data(const QModelIndex &index, int role) const +{ + IndexId id; + int fieldIdx = 0; + + if (!index.isValid()) + return QVariant(); + + id.w = index.internalId(); + + if (id.ws.type == ITYP_FIELD) + { + const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); + int n = index.row() + 1; + + while (n) + { + if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) + n--; + fieldIdx++; + } + fieldIdx--; + } + + // FIXME(HI): Relook at this completely + if (role == Qt::UserRole) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldFrameValue); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QByteArray(); + } + + // FIXME: Use a new enum here instead of UserRole + if (role == (Qt::UserRole+1)) + { + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return mSelectedProtocols.at(id.ws.protocol)-> + protocolFrameValue().size(); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData( + fieldIdx, AbstractProtocol::FieldBitSize); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + switch(id.ws.type) + { + case ITYP_PROTOCOL: + return QString("%1 (%2)") + .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) + .arg(mSelectedProtocols.at(id.ws.protocol)->name()); + + case ITYP_FIELD: + return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldName).toString() + QString(" : ") + + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, + AbstractProtocol::FieldTextValue).toString(); + + default: + qWarning("%s: Unhandled ItemType", __FUNCTION__); + } + + Q_ASSERT(1 == 1); // Unreachable code + + return QVariant(); +} diff --git a/client/packetmodel.h b/client/packetmodel.h new file mode 100644 index 0000000..08dcea9 --- /dev/null +++ b/client/packetmodel.h @@ -0,0 +1,61 @@ +/* +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 +*/ + +#ifndef _PACKET_MODEL_H +#define _PACKET_MODEL_H + +#include + +class ProtocolListIterator; +class AbstractProtocol; + +class PacketModel: public QAbstractItemModel +{ + +public: + PacketModel(QObject *parent = 0); + void setSelectedProtocols(ProtocolListIterator &iter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, + int /*role= Qt::DisplayRole*/) const { + return QVariant(); + } + QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &index) const; + +private: + typedef union _IndexId + { + quint32 w; + struct + { + quint16 type; +#define ITYP_PROTOCOL 1 +#define ITYP_FIELD 2 + quint16 protocol; // protocol is valid for both ITYPs + } ws; + } IndexId; + + QList mSelectedProtocols; +}; +#endif + diff --git a/client/port.cpp b/client/port.cpp new file mode 100644 index 0000000..e9e5915 --- /dev/null +++ b/client/port.cpp @@ -0,0 +1,573 @@ +/* +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 +*/ + +#include "port.h" + +#include "abstractfileformat.h" + +#include +#include +#include +#include +#include +#include + +extern QMainWindow *mainWindow; + +uint Port::mAllocStreamId = 0; + +static const int kEthOverhead = 20; + +uint Port::newStreamId() +{ + return mAllocStreamId++; +} + +Port::Port(quint32 id, quint32 portGroupId) +{ + mPortId = id; + d.mutable_port_id()->set_id(id); + stats.mutable_port_id()->set_id(id); + mPortGroupId = portGroupId; + capFile_ = NULL; +} + +Port::~Port() +{ + qDebug("%s", __FUNCTION__); + while (!mStreams.isEmpty()) + delete mStreams.takeFirst(); +} + +void Port::updatePortConfig(OstProto::Port *port) +{ + d.MergeFrom(*port); +} + +void Port::updateStreamOrdinalsFromIndex() +{ + for (int i=0; i < mStreams.size(); i++) + mStreams[i]->setOrdinal(i); +} + +void Port::reorderStreamsByOrdinals() +{ + qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); +} + +void Port::recalculateAverageRates() +{ + double pps = 0; + double bps = 0; + int n = 0; + + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); + +} + +void Port::setAveragePacketRate(double packetsPerSec) +{ + double rate = 0; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) * + (packetsPerSec - avgPacketsPerSec_)); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + + emit portRateChanged(mPortGroupId, mPortId); +} + +void Port::setAverageBitRate(double bitsPerSec) +{ + double rate = 0; + double pps = 0; + double bps = 0; + int n = 0; + + qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); + qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + foreach (Stream* s, mStreams) + { + if (!s->isEnabled()) + continue; + + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); + qDebug("rate = %g", rate); + break; + case OstProto::kInterleavedTransmit: + rate = s->averagePacketRate() + + ((s->averagePacketRate()/avgPacketsPerSec_) + * ((bitsPerSec - avgBitsPerSec_) + / ((s->frameLenAvg()+kEthOverhead)*8))); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + qDebug("cur stream pps = %g", s->averagePacketRate()); + + s->setAveragePacketRate(rate); + + qDebug("new stream pps = %g", s->averagePacketRate()); + + double r = s->averagePacketRate(); + pps += r; + bps += r * (s->frameLenAvg() + kEthOverhead) * 8; + n++; + + if ((transmitMode() == OstProto::kSequentialTransmit) + && (s->nextWhat() == Stream::e_nw_stop)) + break; + } + + if (n) + { + switch (transmitMode()) + { + case OstProto::kSequentialTransmit: + avgPacketsPerSec_ = pps/n; + avgBitsPerSec_ = bps/n; + break; + case OstProto::kInterleavedTransmit: + avgPacketsPerSec_ = pps; + avgBitsPerSec_ = bps; + break; + default: + Q_ASSERT(false); // Unreachable!! + } + numActiveStreams_ = n; + } + else + avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; + + qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, + avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); + emit portRateChanged(mPortGroupId, mPortId); +} + +bool Port::newStreamAt(int index, OstProto::Stream const *stream) +{ + Stream *s = new Stream; + + if (index > mStreams.size()) + return false; + + if (stream) + s->protoDataCopyFrom(*stream); + + s->setId(newStreamId()); + mStreams.insert(index, s); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::deleteStreamAt(int index) +{ + if (index >= mStreams.size()) + return false; + + delete mStreams.takeAt(index); + updateStreamOrdinalsFromIndex(); + recalculateAverageRates(); + + return true; +} + +bool Port::insertStream(uint streamId) +{ + Stream *s = new Stream; + + s->setId(streamId); + + // FIXME(MED): If a stream with id already exists, what do we do? + mStreams.append(s); + + // Update mAllocStreamId to take into account the stream id received + // from server + if (mAllocStreamId <= streamId) + mAllocStreamId = streamId + 1; + + return true; +} + +bool Port::updateStream(uint streamId, OstProto::Stream *stream) +{ + int i, streamIndex; + + for (i = 0; i < mStreams.size(); i++) + { + if (streamId == mStreams[i]->id()) + goto _found; + } + + qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); + return false; + +_found: + streamIndex = i; + + mStreams[streamIndex]->protoDataCopyFrom(*stream); + reorderStreamsByOrdinals(); + + return true; +} + +void Port::getDeletedStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mLastSyncStreamList.size(); i++) + { + int j; + + for (j = 0; j < mStreams.size(); j++) + { + if (mLastSyncStreamList[i] == mStreams[j]->id()) + break; + } + + if (j < mStreams.size()) + { + // stream still exists! + continue; + } + else + { + // stream has been deleted since last sync + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mLastSyncStreamList.at(i)); + } + } +} + +void Port::getNewStreamsSinceLastSync( + OstProto::StreamIdList &streamIdList) +{ + streamIdList.clear_stream_id(); + for (int i = 0; i < mStreams.size(); i++) + { + if (mLastSyncStreamList.contains(mStreams[i]->id())) + { + // existing stream! + continue; + } + else + { + // new stream! + OstProto::StreamId *s; + + s = streamIdList.add_stream_id(); + s->set_id(mStreams[i]->id()); + } + } +} + +void Port::getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList) +{ + qDebug("In %s", __FUNCTION__); + + //streamConfigList.mutable_port_id()->set_id(mPortId); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s; + + s = streamConfigList.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + } + qDebug("Done %s", __FUNCTION__); +} + +void Port::when_syncComplete() +{ + //reorderStreamsByOrdinals(); + + mLastSyncStreamList.clear(); + for (int i=0; iid()); +} + +void Port::updateStats(OstProto::PortStats *portStats) +{ + OstProto::PortState oldState; + + oldState = stats.state(); + stats.MergeFrom(*portStats); + + if (oldState.link_state() != stats.state().link_state()) + { + qDebug("portstate changed"); + emit portDataChanged(mPortGroupId, mPortId); + } +} + +bool Port::openStreams(QString fileName, bool append, QString &error) +{ + bool ret = false; + QDialog *optDialog; + QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); + OstProto::StreamConfigList streams; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); + + if (fmt == NULL) + goto _fail; + + if ((optDialog = fmt->openOptionsDialog())) + { + int ret; + optDialog->setParent(mainWindow, Qt::Dialog); + ret = optDialog->exec(); + optDialog->setParent(0, Qt::Dialog); + if (ret == QDialog::Rejected) + goto _user_opt_cancel; + } + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + fmt->openStreamsOffline(fileName, streams, error); + qDebug("after open offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + if (!fmt->result()) + goto _fail; + + // process any remaining events posted from the thread + for (int i = 0; i < 10; i++) + qApp->processEvents(); + + if (!append) + { + int n = numStreams(); + + progress.setLabelText("Deleting existing streams..."); + progress.setRange(0, n); + for (int i = 0; i < n; i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + deleteStreamAt(0); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + } + + progress.setLabelText("Constructing new streams..."); + progress.setRange(0, streams.stream_size()); + for (int i = 0; i < streams.stream_size(); i++) + { + if (progress.wasCanceled()) + goto _user_cancel; + newStreamAt(mStreams.size(), &streams.stream(i)); + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + +_user_cancel: + emit streamListChanged(mPortGroupId, mPortId); +_user_opt_cancel: + ret = true; + +_fail: + progress.close(); + mainWindow->setEnabled(true); + recalculateAverageRates(); + return ret; +} + +bool Port::saveStreams(QString fileName, QString fileType, QString &error) +{ + bool ret = false; + QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType); + OstProto::StreamConfigList streams; + + if (fmt == NULL) + goto _fail; + + progress.setAutoReset(false); + progress.setAutoClose(false); + progress.setMinimumDuration(0); + progress.show(); + + mainWindow->setDisabled(true); + progress.setEnabled(true); // to override the mainWindow disable + + connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); + connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); + connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); + connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); + + progress.setLabelText("Preparing Streams..."); + progress.setRange(0, mStreams.size()); + streams.mutable_port_id()->set_id(0); + for (int i = 0; i < mStreams.size(); i++) + { + OstProto::Stream *s = streams.add_stream(); + mStreams[i]->protoDataCopyInto(*s); + + if (progress.wasCanceled()) + goto _user_cancel; + progress.setValue(i); + if (i % 32 == 0) + qApp->processEvents(); + } + + fmt->saveStreamsOffline(streams, fileName, error); + qDebug("after save offline"); + + while (!fmt->isFinished()) + qApp->processEvents(); + qDebug("wait over for offline operation"); + + ret = fmt->result(); + goto _exit; + +_user_cancel: + goto _exit; + +_fail: + error = QString("Unsupported File Type - %1").arg(fileType); + goto _exit; + +_exit: + progress.close(); + mainWindow->setEnabled(true); + return ret; +} diff --git a/client/port.h b/client/port.h new file mode 100644 index 0000000..6df7a98 --- /dev/null +++ b/client/port.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _PORT_H +#define _PORT_H + +#include +#include +#include +#include + +#include "stream.h" + +//class StreamModel; + +class Port : public QObject { + + Q_OBJECT + + static uint mAllocStreamId; + + OstProto::Port d; + OstProto::PortStats stats; + QTemporaryFile *capFile_; + + // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' + quint32 mPortId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + double avgPacketsPerSec_; + double avgBitsPerSec_; + int numActiveStreams_; + + QList mLastSyncStreamList; + QList mStreams; // sorted by stream's ordinal value + + uint newStreamId(); + void updateStreamOrdinalsFromIndex(); + void reorderStreamsByOrdinals(); + + +public: + enum AdminStatus { AdminDisable, AdminEnable }; + + // FIXME(HIGH): default args is a hack for QList operations on Port + Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); + ~Port(); + + quint32 portGroupId() const { return mPortGroupId; } + const QString& userAlias() const { return mUserAlias; } + + quint32 id() const + { return d.port_id().id(); } + const QString name() const + { return QString().fromStdString(d.name()); } + const QString description() const + { return QString().fromStdString(d.description()); } + const QString notes() const + { return QString().fromStdString(d.notes()); } + AdminStatus adminStatus() + { return (d.is_enabled()?AdminEnable:AdminDisable); } + bool hasExclusiveControl() + { return d.is_exclusive_control(); } + OstProto::TransmitMode transmitMode() + { return d.transmit_mode(); } + double averagePacketRate() + { return avgPacketsPerSec_; } + double averageBitRate() + { return avgBitsPerSec_; } + + //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } + void setAlias(QString &alias) { mUserAlias = alias; } + //void setExclusive(bool flag); + + int numStreams() { return mStreams.size(); } + Stream* streamByIndex(int index) + { + Q_ASSERT(index < mStreams.size()); + return mStreams[index]; + } + OstProto::LinkState linkState() + { return stats.state().link_state(); } + + OstProto::PortStats getStats() { return stats; } + QTemporaryFile* getCaptureFile() + { + delete capFile_; + capFile_ = new QTemporaryFile(QString(QDir::tempPath()) + .append("/") + .append(name()) + .append(".XXXXXX")); + return capFile_; + } + + // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal + void updatePortConfig(OstProto::Port *port); + + //! Used by StreamModel + //@{ + bool newStreamAt(int index, OstProto::Stream const *stream = NULL); + bool deleteStreamAt(int index); + //@} + + //! Used by MyService::Stub to update from config received from server + //@{ + bool insertStream(uint streamId); + bool updateStream(uint streamId, OstProto::Stream *stream); + //@} + + void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); + void getModifiedStreamsSinceLastSync( + OstProto::StreamConfigList &streamConfigList); + + void when_syncComplete(); + + void setAveragePacketRate(double packetsPerSec); + void setAverageBitRate(double bitsPerSec); + // FIXME(MED): Bad Hack! port should not need an external trigger to + // recalculate - refactor client side domain objects and model objects + void recalculateAverageRates(); + void updateStats(OstProto::PortStats *portStats); + + bool openStreams(QString fileName, bool append, QString &error); + bool saveStreams(QString fileName, QString fileType, QString &error); + +signals: + void portRateChanged(int portGroupId, int portId); + void portDataChanged(int portGroupId, int portId); + void streamListChanged(int portGroupId, int portId); + +}; + +#endif diff --git a/client/portconfigdialog.cpp b/client/portconfigdialog.cpp new file mode 100644 index 0000000..17adeb5 --- /dev/null +++ b/client/portconfigdialog.cpp @@ -0,0 +1,57 @@ +/* +Copyright (C) 2011 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 "portconfigdialog.h" + +PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent) + : QDialog(parent), portConfig_(portConfig) +{ + qDebug("In %s", __FUNCTION__); + + setupUi(this); + + switch(portConfig_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + sequentialStreamsButton->setChecked(true); + break; + case OstProto::kInterleavedTransmit: + interleavedStreamsButton->setChecked(true); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } + + exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); +} + +void PortConfigDialog::accept() +{ + if (sequentialStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kSequentialTransmit); + else if (interleavedStreamsButton->isChecked()) + portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit); + else + Q_ASSERT(false); // Unreachable!!! + + portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked()); + + QDialog::accept(); +} diff --git a/client/portconfigdialog.h b/client/portconfigdialog.h new file mode 100644 index 0000000..4d14b0b --- /dev/null +++ b/client/portconfigdialog.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PORT_CONFIG_DIALOG_H +#define _PORT_CONFIG_DIALOG_H + +#include "ui_portconfigdialog.h" +#include "protocol.pb.h" +#include + +class PortConfigDialog : public QDialog, public Ui::PortConfigDialog +{ +public: + PortConfigDialog(OstProto::Port &portConfig, QWidget *parent); + +private: + virtual void accept(); + + OstProto::Port &portConfig_; +}; + +#endif + diff --git a/client/portconfigdialog.ui b/client/portconfigdialog.ui new file mode 100644 index 0000000..954b827 --- /dev/null +++ b/client/portconfigdialog.ui @@ -0,0 +1,109 @@ + + PortConfigDialog + + + + 0 + 0 + 244 + 160 + + + + Port Config + + + + + + Transmit Mode + + + + + + Sequential Streams + + + true + + + + + + + Interleaved Streams + + + + + + + + + + Exclusive Control + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PortConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portgroup.cpp b/client/portgroup.cpp new file mode 100644 index 0000000..8e20735 --- /dev/null +++ b/client/portgroup.cpp @@ -0,0 +1,896 @@ +/* +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 +*/ + +#include "portgroup.h" + +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::google::protobuf::NewCallback; + +extern QMainWindow *mainWindow; +extern char *version; + +quint32 PortGroup::mPortGroupAllocId = 0; + +PortGroup::PortGroup(QHostAddress ip, quint16 port) +{ + // Allocate an id for self + mPortGroupId = PortGroup::mPortGroupAllocId++; + + portIdList_ = new OstProto::PortIdList; + portStatsList_ = new OstProto::PortStatsList; + + statsController = new PbRpcController(portIdList_, portStatsList_); + isGetStatsPending_ = false; + + compat = kUnknown; + + reconnect = false; + reconnectAfter = kMinReconnectWaitTime; + reconnectTimer = new QTimer(this); + reconnectTimer->setSingleShot(true); + connect(reconnectTimer, SIGNAL(timeout()), + this, SLOT(on_reconnectTimer_timeout())); + + rpcChannel = new PbRpcChannel(ip, port); + serviceStub = new OstProto::OstService::Stub(rpcChannel); + + // FIXME(LOW):Can't for my life figure out why this ain't working! + //QMetaObject::connectSlotsByName(this); + connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); + connect(rpcChannel, SIGNAL(connected()), + this, SLOT(on_rpcChannel_connected())); + connect(rpcChannel, SIGNAL(disconnected()), + this, SLOT(on_rpcChannel_disconnected())); + connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); + + connect(this, SIGNAL(portListChanged(quint32)), + this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); +} + +PortGroup::~PortGroup() +{ + qDebug("PortGroup Destructor"); + // Disconnect and free rpc channel etc. + PortGroup::disconnectFromHost(); + delete serviceStub; + delete rpcChannel; + delete statsController; +} + + +// ------------------------------------------------ +// Slots +// ------------------------------------------------ +void PortGroup::on_reconnectTimer_timeout() +{ + reconnectAfter *= 2; + if (reconnectAfter > kMaxReconnectWaitTime) + reconnectAfter = kMaxReconnectWaitTime; + + connectToHost(); +} + +void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) +{ + qDebug("state changed %d", state); + + switch (state) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + break; + + default: + emit portGroupDataChanged(mPortGroupId); + } +} + +void PortGroup::on_rpcChannel_connected() +{ + OstProto::VersionInfo *verInfo = new OstProto::VersionInfo; + OstProto::VersionCompatibility *verCompat = + new OstProto::VersionCompatibility; + + qDebug("connected\n"); + emit portGroupDataChanged(mPortGroupId); + + reconnectAfter = kMinReconnectWaitTime; + + qDebug("requesting version check ..."); + verInfo->set_version(version); + + PbRpcController *controller = new PbRpcController(verInfo, verCompat); + serviceStub->checkVersion(controller, verInfo, verCompat, + NewCallback(this, &PortGroup::processVersionCompatibility, + controller)); +} + +void PortGroup::processVersionCompatibility(PbRpcController *controller) +{ + OstProto::VersionCompatibility *verCompat + = static_cast(controller->response()); + + Q_ASSERT(verCompat != NULL); + + qDebug("got version result ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _error_exit; + } + + if (verCompat->result() == OstProto::VersionCompatibility::kIncompatible) { + qWarning("incompatible version %s (%s)", version, + qPrintable(QString::fromStdString(verCompat->notes()))); + compat = kIncompatible; + emit portGroupDataChanged(mPortGroupId); + goto _error_exit; + } + + compat = kCompatible; + + { + OstProto::Void *void_ = new OstProto::Void; + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + + qDebug("requesting portlist ..."); + PbRpcController *controller = new PbRpcController(void_, portIdList); + serviceStub->getPortIdList(controller, void_, portIdList, + NewCallback(this, &PortGroup::processPortIdList, controller)); + } + +_error_exit: + delete controller; +} + +void PortGroup::on_rpcChannel_disconnected() +{ + qDebug("disconnected\n"); + emit portListAboutToBeChanged(mPortGroupId); + + while (!mPorts.isEmpty()) + delete mPorts.takeFirst(); + + emit portListChanged(mPortGroupId); + emit portGroupDataChanged(mPortGroupId); + + isGetStatsPending_ = false; + + if (reconnect) + { + qDebug("starting reconnect timer for %d ms ...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s: error %d", __FUNCTION__, socketError); + emit portGroupDataChanged(mPortGroupId); + + if (socketError == QAbstractSocket::RemoteHostClosedError) + reconnect = false; + + qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); + if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) + { + qDebug("starting reconnect timer for %d ms...", reconnectAfter); + reconnectTimer->start(reconnectAfter); + } +} + +void PortGroup::when_portListChanged(quint32 /*portGroupId*/) +{ + if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) + { + QMessageBox::warning(NULL, tr("No ports in portgroup"), + QString("The portgroup %1:%2 does not contain any ports!\n\n" + "Packet Transmit/Capture requires elevated privileges. " + "Please ensure that you are running 'drone' - the server " + "component of Ostinato with admin/root OR setuid privilege.\n\n" + "For more information see " + "http://code.google.com/p/ostinato/wiki/FAQ#" + "Q._Port_group_has_no_interfaces") + .arg(serverAddress().toString()) + .arg(int(serverPort()))); + } +} + +void PortGroup::processPortIdList(PbRpcController *controller) +{ + OstProto::PortIdList *portIdList + = static_cast(controller->response()); + + Q_ASSERT(portIdList != NULL); + + qDebug("got a portlist ..."); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _error_exit; + } + + emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portIdList->port_id_size(); i++) + { + Port *p; + + p = new Port(portIdList->port_id(i).id(), mPortGroupId); + connect(p, SIGNAL(portDataChanged(int, int)), + this, SIGNAL(portGroupDataChanged(int, int))); + qDebug("before port append\n"); + mPorts.append(p); + } + + emit portListChanged(mPortGroupId); + + portIdList_->CopyFrom(*portIdList); + + // Request PortConfigList + { + qDebug("requesting port config list ..."); + OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); + PbRpcController *controller2 = new PbRpcController(portIdList2, + portConfigList); + + portIdList2->CopyFrom(*portIdList); + + serviceStub->getPortConfig(controller, portIdList2, portConfigList, + NewCallback(this, &PortGroup::processPortConfigList, controller2)); + + goto _exit; + } + +_error_exit: +_exit: + delete controller; +} + +void PortGroup::processPortConfigList(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _error_exit; + } + + //emit portListAboutToBeChanged(mPortGroupId); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + } + + //emit portListChanged(mPortGroupId); + + // FIXME: check if we need new signals since we are not changing the + // number of ports, just the port data + + if (numPorts() > 0) + getStreamIdList(); + +_error_exit: + delete controller; +} + +void PortGroup::when_configApply(int portIndex) +{ + OstProto::StreamIdList *streamIdList; + OstProto::StreamConfigList *streamConfigList; + OstProto::Ack *ack; + PbRpcController *controller; + + Q_ASSERT(portIndex < mPorts.size()); + + if (state() != QAbstractSocket::ConnectedState) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + qDebug("applying 'deleted streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); + + serviceStub->deleteStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); + + qDebug("applying 'new streams' ..."); + streamIdList = new OstProto::StreamIdList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamIdList, ack); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); + + serviceStub->addStream(controller, streamIdList, ack, + NewCallback(this, &PortGroup::processAddStreamAck, controller)); + + qDebug("applying 'modified streams' ..."); + streamConfigList = new OstProto::StreamConfigList; + ack = new OstProto::Ack; + controller = new PbRpcController(streamConfigList, ack); + + streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); + + serviceStub->modifyStream(controller, streamConfigList, ack, + NewCallback(this, &PortGroup::processModifyStreamAck, + portIndex, controller)); +} + +void PortGroup::processAddStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processDeleteStreamAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + delete controller; +} + +void PortGroup::processModifyStreamAck(int portIndex, + PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + qDebug("apply completed"); + mPorts[portIndex]->when_syncComplete(); + + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + + delete controller; +} + +void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) +{ + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + OstProto::Ack *ack = new OstProto::Ack; + + qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); + + Q_ASSERT(portIndex < mPorts.size()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mainWindow->setDisabled(true); + + OstProto::Port *port = portConfigList->add_port(); + port->CopyFrom(portConfig); + port->mutable_port_id()->set_id(mPorts[portIndex]->id()); + + PbRpcController *controller = new PbRpcController(portConfigList, ack); + serviceStub->modifyPort(controller, portConfigList, ack, + NewCallback(this, &PortGroup::processModifyPortAck, controller)); +} + +void PortGroup::processModifyPortAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _exit; + } + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; + PbRpcController *controller2 = new PbRpcController(portIdList, + portConfigList); + + OstProto::PortId *portId = portIdList->add_port_id(); + portId->CopyFrom(static_cast + (controller->request())->mutable_port(0)->port_id()); + + serviceStub->getPortConfig(controller, portIdList, portConfigList, + NewCallback(this, &PortGroup::processUpdatedPortConfig, + controller2)); + } +_exit: + delete controller; +} + +void PortGroup::processUpdatedPortConfig(PbRpcController *controller) +{ + OstProto::PortConfigList *portConfigList + = static_cast(controller->response()); + + qDebug("In %s", __FUNCTION__); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _exit; + } + + if (portConfigList->port_size() != 1) + qDebug("port size = %d (expected = 1)", portConfigList->port_size()); + + for(int i = 0; i < portConfigList->port_size(); i++) + { + uint id; + + id = portConfigList->port(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); + + emit portGroupDataChanged(mPortGroupId, id); + } + + +_exit: + mainWindow->setEnabled(true); + QApplication::restoreOverrideCursor(); + delete controller; +} + +void PortGroup::getStreamIdList() +{ + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + PbRpcController *controller = new PbRpcController(portId, streamIdList); + + portId->set_id(mPorts[portIndex]->id()); + + serviceStub->getStreamIdList(controller, portId, streamIdList, + NewCallback(this, &PortGroup::processStreamIdList, + portIndex, controller)); + } +} + +void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) +{ + OstProto::StreamIdList *streamIdList + = static_cast(controller->response()); + + qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamIdList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamIdList->stream_id_size(); i++) + { + uint streamId; + + streamId = streamIdList->stream_id(i).id(); + mPorts[portIndex]->insertStream(streamId); + } + + mPorts[portIndex]->when_syncComplete(); + + // Are we done for all ports? + if (numPorts() && portIndex >= (numPorts()-1)) + { + // FIXME(HI): some way to reset streammodel + getStreamConfigList(); + } + +_exit: + delete controller; +} + +void PortGroup::getStreamConfigList() +{ + qDebug("requesting stream config list ..."); + + for (int portIndex = 0; portIndex < numPorts(); portIndex++) + { + OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; + OstProto::StreamConfigList *streamConfigList + = new OstProto::StreamConfigList; + PbRpcController *controller = new PbRpcController( + streamIdList, streamConfigList); + + streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); + for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) + { + OstProto::StreamId *s = streamIdList->add_stream_id(); + s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); + } + + serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, + NewCallback(this, &PortGroup::processStreamConfigList, + portIndex, controller)); + } +} + +void PortGroup::processStreamConfigList(int portIndex, + PbRpcController *controller) +{ + OstProto::StreamConfigList *streamConfigList + = static_cast(controller->response()); + + qDebug("In %s", __PRETTY_FUNCTION__); + + Q_ASSERT(portIndex < numPorts()); + + if (controller->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(controller->ErrorString())); + goto _exit; + } + + Q_ASSERT(portIndex < numPorts()); + + if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) + { + qDebug("Invalid portId %d (expected %d) received for portIndex %d", + streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); + goto _exit; + } + + for(int i = 0; i < streamConfigList->stream_size(); i++) + { + uint streamId; + + streamId = streamConfigList->stream(i).stream_id().id(); + mPorts[portIndex]->updateStream(streamId, + streamConfigList->mutable_stream(i)); + } + + // Are we done for all ports? + if (portIndex >= numPorts()) + { + // FIXME(HI): some way to reset streammodel + } + +_exit: + delete controller; +} + +void PortGroup::startTx(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (portList == NULL) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startTransmit(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopTx(QList *portList) +{ + + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopTransmit(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopTxAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopTxAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::startCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->startCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStartCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStartCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::stopCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + return; + + if ((portList == NULL) || (portList->size() == 0)) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + + serviceStub->stopCapture(controller, portIdList, ack, + NewCallback(this, &PortGroup::processStopCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processStopCaptureAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + delete controller; +} + +void PortGroup::viewCapture(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if ((portList == NULL) || (portList->size() != 1)) + goto _exit; + + + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = new OstProto::PortId; + OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; + PbRpcController *controller = new PbRpcController(portId, buf); + QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); + + portId->set_id(portList->at(i)); + + capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); + qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); + controller->setBinaryBlob(capFile); + + serviceStub->getCaptureBuffer(controller, portId, buf, + NewCallback(this, &PortGroup::processViewCaptureAck, controller)); + } +_exit: + return; +} + +void PortGroup::processViewCaptureAck(PbRpcController *controller) +{ + QFile *capFile = static_cast(controller->binaryBlob()); + + QString viewer = appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString(); + + qDebug("In %s", __FUNCTION__); + + capFile->flush(); + capFile->close(); + + if (!QFile::exists(viewer)) + { + QMessageBox::warning(NULL, "Can't find Wireshark", + viewer + QString(" does not exist!\n\nPlease correct the path" + " to Wireshark in the Preferences.")); + goto _exit; + } + + if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) + qDebug("Failed starting Wireshark"); + +_exit: + delete controller; +} + +void PortGroup::getPortStats() +{ + //qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + if (numPorts() <= 0) + goto _exit; + + if (isGetStatsPending_) + goto _exit; + + statsController->Reset(); + isGetStatsPending_ = true; + serviceStub->getStats(statsController, + static_cast(statsController->request()), + static_cast(statsController->response()), + NewCallback(this, &PortGroup::processPortStatsList)); + +_exit: + return; +} + +void PortGroup::processPortStatsList() +{ + //qDebug("In %s", __FUNCTION__); + + if (statsController->Failed()) + { + qDebug("%s: rpc failed(%s)", __FUNCTION__, + qPrintable(statsController->ErrorString())); + goto _error_exit; + } + + for(int i = 0; i < portStatsList_->port_stats_size(); i++) + { + uint id = portStatsList_->port_stats(i).port_id().id(); + // FIXME: don't mix port id & index into mPorts[] + mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); + } + + emit statsChanged(mPortGroupId); + +_error_exit: + isGetStatsPending_ = false; +} + +void PortGroup::clearPortStats(QList *portList) +{ + qDebug("In %s", __FUNCTION__); + + if (state() != QAbstractSocket::ConnectedState) + goto _exit; + + { + OstProto::PortIdList *portIdList = new OstProto::PortIdList; + OstProto::Ack *ack = new OstProto::Ack; + PbRpcController *controller = new PbRpcController(portIdList, ack); + + if (portList == NULL) + portIdList->CopyFrom(*portIdList_); + else + { + for (int i = 0; i < portList->size(); i++) + { + OstProto::PortId *portId = portIdList->add_port_id(); + portId->set_id(portList->at(i)); + } + } + + serviceStub->clearStats(controller, portIdList, ack, + NewCallback(this, &PortGroup::processClearStatsAck, controller)); + } +_exit: + return; +} + +void PortGroup::processClearStatsAck(PbRpcController *controller) +{ + qDebug("In %s", __FUNCTION__); + + // Refresh stats immediately after a stats clear/reset + getPortStats(); + + delete controller; +} + diff --git a/client/portgroup.h b/client/portgroup.h new file mode 100644 index 0000000..ccb8db8 --- /dev/null +++ b/client/portgroup.h @@ -0,0 +1,157 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_H +#define _PORT_GROUP_H + +#include "port.h" +#include +#include + +#include "../common/protocol.pb.h" +#include "pbrpcchannel.h" + +/* TODO +HIGH +MED +LOW +- Allow hostnames in addition to IP Address as "server address" +*/ + +#define DEFAULT_SERVER_PORT 7878 + +class QFile; +class QTimer; + +class PortGroup : public QObject { + Q_OBJECT + +private: + enum { kIncompatible, kCompatible, kUnknown } compat; + static quint32 mPortGroupAllocId; + quint32 mPortGroupId; + QString mUserAlias; // user defined + + bool reconnect; + int reconnectAfter; // time in milliseconds + static const int kMinReconnectWaitTime = 2000; // ms + static const int kMaxReconnectWaitTime = 60000; // ms + QTimer *reconnectTimer; + PbRpcChannel *rpcChannel; + PbRpcController *statsController; + bool isGetStatsPending_; + + OstProto::OstService::Stub *serviceStub; + + OstProto::PortIdList *portIdList_; + OstProto::PortStatsList *portStatsList_; + +public: // FIXME(HIGH): member access + QList mPorts; + +public: + PortGroup(QHostAddress ip = QHostAddress::LocalHost, + quint16 port = DEFAULT_SERVER_PORT); + ~PortGroup(); + + void connectToHost() { + reconnect = true; + compat = kUnknown; + rpcChannel->establish(); + } + void connectToHost(QHostAddress ip, quint16 port) { + reconnect = true; + compat = kUnknown; + rpcChannel->establish(ip, port); + } + void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } + + int numPorts() const { return mPorts.size(); } + quint32 id() const { return mPortGroupId; } + + const QString& userAlias() const { return mUserAlias; } + void setUserAlias(QString alias) { mUserAlias = alias; }; + + const QHostAddress& serverAddress() const + { return rpcChannel->serverAddress(); } + quint16 serverPort() const + { return rpcChannel->serverPort(); } + QAbstractSocket::SocketState state() const { + if (compat == kIncompatible) + return QAbstractSocket::SocketState(-1); + return rpcChannel->state(); + } + + void processVersionCompatibility(PbRpcController *controller); + void processPortIdList(PbRpcController *controller); + void processPortConfigList(PbRpcController *controller); + + void processAddStreamAck(PbRpcController *controller); + void processDeleteStreamAck(PbRpcController *controller); + void processModifyStreamAck(int portIndex, PbRpcController *controller); + + void modifyPort(int portId, OstProto::Port portConfig); + void processModifyPortAck(PbRpcController *controller); + void processUpdatedPortConfig(PbRpcController *controller); + + void getStreamIdList(); + void processStreamIdList(int portIndex, PbRpcController *controller); + void getStreamConfigList(); + void processStreamConfigList(int portIndex, PbRpcController *controller); + + void processModifyStreamAck(OstProto::Ack *ack); + + void startTx(QList *portList = NULL); + void processStartTxAck(PbRpcController *controller); + void stopTx(QList *portList = NULL); + void processStopTxAck(PbRpcController *controller); + + void startCapture(QList *portList = NULL); + void processStartCaptureAck(PbRpcController *controller); + void stopCapture(QList *portList = NULL); + void processStopCaptureAck(PbRpcController *controller); + void viewCapture(QList *portList = NULL); + void processViewCaptureAck(PbRpcController *controller); + + void getPortStats(); + void processPortStatsList(); + void clearPortStats(QList *portList = NULL); + void processClearStatsAck(PbRpcController *controller); + +signals: + void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); + void portListAboutToBeChanged(quint32 portGroupId); + void portListChanged(quint32 portGroupId); + void statsChanged(quint32 portGroupId); + +private slots: + void on_reconnectTimer_timeout(); + void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); + void on_rpcChannel_connected(); + void on_rpcChannel_disconnected(); + void on_rpcChannel_error(QAbstractSocket::SocketError socketError); + + void when_portListChanged(quint32 portGroupId); + +public slots: + void when_configApply(int portIndex); + +}; + +#endif diff --git a/client/portgrouplist.cpp b/client/portgrouplist.cpp new file mode 100644 index 0000000..cfdc74b --- /dev/null +++ b/client/portgrouplist.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "portgrouplist.h" + +// TODO(LOW): Remove +#include + +PortGroupList::PortGroupList() + : mPortGroupListModel(this), + mStreamListModel(this), + mPortStatsModel(this, this) +{ + PortGroup *pg; + +#ifdef QT_NO_DEBUG + streamModelTester_ = NULL; + portModelTester_ = NULL; + portStatsModelTester_ = NULL; +#else + streamModelTester_ = new ModelTest(getStreamModel()); + portModelTester_ = new ModelTest(getPortModel()); + portStatsModelTester_ = new ModelTest(getPortStatsModel()); +#endif + + // Add the "Local" Port Group + pg = new PortGroup; + addPortGroup(*pg); +} + +PortGroupList::~PortGroupList() +{ + delete portStatsModelTester_; + delete portModelTester_; + delete streamModelTester_; + + while (!mPortGroups.isEmpty()) + delete mPortGroups.takeFirst(); + +} + +bool PortGroupList::isPortGroup(const QModelIndex& index) +{ + return mPortGroupListModel.isPortGroup(index); +} + +bool PortGroupList::isPort(const QModelIndex& index) +{ + return mPortGroupListModel.isPort(index); +} + +PortGroup& PortGroupList::portGroup(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPortGroup(index)); + + return *(mPortGroups[index.row()]); +} + +Port& PortGroupList::port(const QModelIndex& index) +{ + Q_ASSERT(mPortGroupListModel.isPort(index)); + return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); +} + +void PortGroupList::addPortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeAppended(); + + connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), + &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); +#if 0 + connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(triggerLayoutChanged())); +#endif + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortGroupListModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(portListChanged(quint32)), + &mPortStatsModel, SLOT(when_portListChanged())); + + connect(&portGroup, SIGNAL(statsChanged(quint32)), + &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); + + mPortGroups.append(&portGroup); + portGroup.connectToHost(); + + mPortGroupListModel.portGroupAppended(); + + mPortStatsModel.when_portListChanged(); +} + +void PortGroupList::removePortGroup(PortGroup &portGroup) +{ + mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); + + PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); + qDebug("after takeAt()"); + mPortGroupListModel.portGroupRemoved(); + + delete pg; + + mPortStatsModel.when_portListChanged(); +} + +//.................... +// Private Methods +//.................... +int PortGroupList::indexOfPortGroup(quint32 portGroupId) +{ + for (int i = 0; i < mPortGroups.size(); i++) { + if (mPortGroups.value(i)->id() == portGroupId) + return i; + } + return -1; +} diff --git a/client/portgrouplist.h b/client/portgrouplist.h new file mode 100644 index 0000000..3083c26 --- /dev/null +++ b/client/portgrouplist.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_GROUP_LIST_H +#define _PORT_GROUP_LIST_H + +#include "portgroup.h" +#include +#include +#include "portmodel.h" +#include "streammodel.h" +#include "portstatsmodel.h" + +class PortModel; +class StreamModel; + +class PortGroupList : public QObject { + + Q_OBJECT + + friend class PortModel; + friend class StreamModel; + friend class PortStatsModel; + + QList mPortGroups; + PortModel mPortGroupListModel; + StreamModel mStreamListModel; + PortStatsModel mPortStatsModel; + + QObject *streamModelTester_; + QObject *portModelTester_; + QObject *portStatsModelTester_; + +// Methods +public: + PortGroupList(); + ~PortGroupList(); + + PortModel* getPortModel() { return &mPortGroupListModel; } + PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } + StreamModel* getStreamModel() { return &mStreamListModel; } + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + PortGroup& portGroup(const QModelIndex& index); + Port& port(const QModelIndex& index); + + int numPortGroups() { return mPortGroups.size(); } + PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } + + void addPortGroup(PortGroup &portGroup); + void removePortGroup(PortGroup &portGroup); + +private: + int indexOfPortGroup(quint32 portGroupId); + +}; + +#endif diff --git a/client/portmodel.cpp b/client/portmodel.cpp new file mode 100644 index 0000000..0ef055e --- /dev/null +++ b/client/portmodel.cpp @@ -0,0 +1,339 @@ +/* +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 +*/ + +#include "portmodel.h" +#include "portgrouplist.h" + +#include +#include + +#if 0 +#define DBG0(x) qDebug(x) +#define DBG1(x, p1) qDebug(x, (p1)) +#else +#define DBG0(x) {} +#define DBG1(x, p1) {} +#endif + +PortModel::PortModel(PortGroupList *p, QObject *parent) + : QAbstractItemModel(parent) +{ + pgl = p; + + portIconFactory[OstProto::LinkStateUnknown][false] = + QIcon(":/icons/bullet_white.png"); + portIconFactory[OstProto::LinkStateDown][false] = + QIcon(":/icons/bullet_red.png"); + portIconFactory[OstProto::LinkStateUp][false] = + QIcon(":/icons/bullet_green.png"); + + for (int linkState = 0; linkState < kLinkStatesCount; linkState++) + { + QPixmap pixmap(":/icons/deco_exclusive.png"); + QPainter painter(&pixmap); + QIcon icon = portIconFactory[linkState][false]; + + painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); + portIconFactory[linkState][true] = QIcon(pixmap); + } +} + +int PortModel::rowCount(const QModelIndex &parent) const +{ + // qDebug("RowCount Enter\n"); + if (!parent.isValid()) + { + // Top Level Item + //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); + return pgl->mPortGroups.size(); + } + // qDebug("RowCount non top %d, %d, %llx\n", + // parent.row(), parent.column(), parent.internalId()); + + quint16 pg = (parent.internalId() >> 16) & 0xFFFF; + quint16 p = parent.internalId() & 0xFFFF; + if (p == 0xFFFF) + { +#if 0 // wrong code? + int count = 0; + foreach(PortGroup *pg, pgl->mPortGroups) + { + count += pg->numPorts(); + } + //qDebug("RowCount (Mid) Exit: %d\n", count); + return count; +#endif + if (parent.column() == 0) + return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); + else + return 0; + } + else + { + // Leaf Item + return 0; + } +} + +int PortModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; // FIXME: hardcoding +} + +Qt::ItemFlags PortModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index); // FIXME: no need for this func +} +QVariant PortModel::data(const QModelIndex &index, int role) const +{ + + DBG0("Enter PortModel data\n"); + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + DBG1("PortModel::data(index).row = %d", index.row()); + DBG1("PortModel::data(index).column = %0d", index.column()); + DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); + + QModelIndex parent = index.parent(); + + if (!parent.isValid()) + { + // Top Level Item - PortGroup + if ((role == Qt::DisplayRole)) + { + DBG0("Exit PortModel data 1\n"); + return QString("Port Group %1: %2 [%3:%4] (%5)"). + arg(pgl->mPortGroups.at(index.row())->id()). + arg(pgl->mPortGroups.at(index.row())->userAlias()). + arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()). + arg(pgl->mPortGroups.at(index.row())->serverPort()). + arg(pgl->mPortGroups.value(index.row())->numPorts()); + } + else if ((role == Qt::DecorationRole)) + { + DBG0("Exit PortModel data 2\n"); + switch(pgl->mPortGroups.at(index.row())->state()) + { + case QAbstractSocket::UnconnectedState: + return QIcon(":/icons/bullet_red.png"); + + case QAbstractSocket::HostLookupState: + return QIcon(":/icons/bullet_yellow.png"); + + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ClosingState: + return QIcon(":/icons/bullet_orange.png"); + + case QAbstractSocket::ConnectedState: + return QIcon(":/icons/bullet_green.png"); + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + return QIcon(":/icons/bullet_error.png"); + } + } + else + { + DBG0("Exit PortModel data 3\n"); + return QVariant(); + } + } + else + { + if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) + { + DBG0("Exit PortModel data 4\n"); + return QVariant(); + } + + Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; + + // Non Top Level - Port + if ((role == Qt::DisplayRole)) + { + // FIXME(LOW) - IP Address below + return QString("Port %1: %2 [%3] (%4)") + .arg(port->id()) + .arg(port->name()) + .arg(QHostAddress("0.0.0.0").toString()) + .arg(port->description()); + } + else if ((role == Qt::DecorationRole)) + { + return portIconFactory[port->linkState()][port->hasExclusiveControl()]; + } + else + { + DBG0("Exit PortModel data 6\n"); + return QVariant(); + } + } + + return QVariant(); +} + +QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QVariant(); + else + return QString("Name"); +} + +QModelIndex PortModel::index (int row, int col, + const QModelIndex & parent) const +{ + if (!hasIndex(row, col, parent)) + return QModelIndex(); + + //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", + // row, col, parent.row(), parent.column(), parent.internalId()); + + if (!parent.isValid()) + { + // Top Level Item + quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; + quint32 id = (pg << 16) | p; + //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } + else + { + quint16 pg = parent.internalId() >> 16; + quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); + quint32 id = (pg << 16) | p; + //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); + + return createIndex(row, col, id); + } +} + +QModelIndex PortModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + //qDebug("parent: R=%d, C=%d ID=%llx\n", + // index.row(), index.column(), index.internalId()); + + quint16 pg = index.internalId() >> 16; + quint16 p = index.internalId() & 0x0000FFFF; + + //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); + + if (p == 0xFFFF) + { + //qDebug("parent ret: NULL\n"); + // Top Level Item - PG + return QModelIndex(); + } + + quint32 id = (pg << 16) | 0xFFFF; + //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); + + return createIndex(pgl->indexOfPortGroup(pg), 0, id); + +} + +bool PortModel::isPortGroup(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) + return true; + else + return false; +} + +bool PortModel::isPort(const QModelIndex& index) +{ + if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) + return true; + else + return false; +} + +quint32 PortModel::portGroupId(const QModelIndex& index) +{ + return (index.internalId()) >> 16 & 0xFFFF; +} + +quint32 PortModel::portId(const QModelIndex& index) +{ + return (index.internalId()) & 0xFFFF; +} + + + +// ---------------------------------------------- +// Slots +// ---------------------------------------------- +void PortModel::when_portGroupDataChanged(int portGroupId, int portId) +{ + QModelIndex index; + int row; + + qDebug("portGroupId = %d, portId = %d", portGroupId, portId); + if (portId == 0xFFFF) + row = pgl->indexOfPortGroup(portGroupId); + else + row = portId; + + index = createIndex(row, 0, (portGroupId << 16) | portId); + + emit dataChanged(index, index); +} + +void PortModel::portGroupAboutToBeAppended() +{ + int row; + + row = pgl->mPortGroups.size(); + beginInsertRows(QModelIndex(), row, row); +} + +void PortModel::portGroupAppended() +{ + endInsertRows(); +} + +void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) +{ + int row; + + row = pgl->mPortGroups.indexOf(portGroup); + beginRemoveRows(QModelIndex(), row, row); +} + +void PortModel::portGroupRemoved() +{ + endRemoveRows(); +} + +void PortModel::when_portListChanged() +{ + reset(); +} diff --git a/client/portmodel.h b/client/portmodel.h new file mode 100644 index 0000000..2027f0b --- /dev/null +++ b/client/portmodel.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _PORT_MODEL_H +#define _PORT_MODEL_H + +#include +#include + +class PortGroupList; +class PortGroup; + +class PortModel : public QAbstractItemModel +{ + Q_OBJECT + + friend class PortGroupList; + +public: + PortModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index (int row, int col, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + + bool isPortGroup(const QModelIndex& index); + bool isPort(const QModelIndex& index); + quint32 portGroupId(const QModelIndex& index); + quint32 portId(const QModelIndex& index); + +private: + PortGroupList *pgl; + static const int kLinkStatesCount = 3; + static const int kExclusiveStatesCount = 2; + QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; + +private slots: + void when_portGroupDataChanged(int portGroupId, int portId); + + void portGroupAboutToBeAppended(); + void portGroupAppended(); + void portGroupAboutToBeRemoved(PortGroup *portGroup); + void portGroupRemoved(); + + void when_portListChanged(); + +#if 0 + void triggerLayoutAboutToBeChanged(); + void triggerLayoutChanged(); +#endif +}; + +#endif diff --git a/client/portstatsfilter.ui b/client/portstatsfilter.ui new file mode 100644 index 0000000..61aa7a7 --- /dev/null +++ b/client/portstatsfilter.ui @@ -0,0 +1,170 @@ + + PortStatsFilterDialog + + + + 0 + 0 + 319 + 193 + + + + Select Ports + + + :/icons/portstats_filter.png + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ExtendedSelection + + + QListView::Static + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + > + + + :/icons/arrow_right.png + + + + + + + < + + + :/icons/arrow_left.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + true + + + false + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QListView::Free + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + lvUnselected + tbSelectIn + tbSelectOut + lvSelected + buttonBox + + + + + + + buttonBox + accepted() + PortStatsFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PortStatsFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/portstatsfilterdialog.cpp b/client/portstatsfilterdialog.cpp new file mode 100644 index 0000000..c2aec10 --- /dev/null +++ b/client/portstatsfilterdialog.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +#include "portstatsfilterdialog.h" + +PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + mUnselected.setSortRole(kLogicalIndex); + mSelected.setSortRole(kVisualIndex); + + 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, kLogicalIndex); + item->setData(initial.indexOf(i), kVisualIndex); + item->setFlags(Qt::ItemIsSelectable + | Qt::ItemIsDragEnabled + //| Qt::ItemIsDropEnabled + | Qt::ItemIsEnabled); + + if (initial.contains(i)) + mSelected.appendRow(item); + else + mUnselected.appendRow(item); + } + mSelected.sort(0); + + // 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(kLogicalIndex).toInt()); + } + *ok = true; + } + + return ret; +} + +void PortStatsFilterDialog::on_tbSelectIn_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + foreach(int row, rows) + { + QList items = mUnselected.takeRow(row); + mSelected.insertRow(insertAt, items); + } +} + +void PortStatsFilterDialog::on_tbSelectOut_clicked() +{ + QList rows; + + foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) + rows.append(idx.row()); + qSort(rows.begin(), rows.end(), qGreater()); + + foreach(int row, rows) + { + QList items = mSelected.takeRow(row); + mUnselected.appendRow(items); + } + + mUnselected.sort(0); +} + +void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) +{ + QList items = mUnselected.takeRow(index.row()); + QModelIndex idx = lvSelected->selectionModel()->currentIndex(); + int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); + + mSelected.insertRow(insertAt, items); +} + +void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) +{ + QList items = mSelected.takeRow(index.row()); + mUnselected.appendRow(items); + mUnselected.sort(0); +} + diff --git a/client/portstatsfilterdialog.h b/client/portstatsfilterdialog.h new file mode 100644 index 0000000..97a0d37 --- /dev/null +++ b/client/portstatsfilterdialog.h @@ -0,0 +1,55 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_FILTER_DIALOG_H +#define _PORT_STATS_FILTER_DIALOG_H + +#include +#include +#include +#include "ui_portstatsfilter.h" +#include "portgrouplist.h" + +class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog +{ + Q_OBJECT + +public: + PortStatsFilterDialog(QWidget *parent = 0); + QList getItemList(bool* ok, QAbstractItemModel *model, + Qt::Orientation orientation = Qt::Vertical, + QList initial = QList()); + +private: + enum ItemRole { + kLogicalIndex = Qt::UserRole + 1, + kVisualIndex + }; + QStandardItemModel mUnselected; + QStandardItemModel mSelected; + +private slots: + void on_tbSelectIn_clicked(); + void on_tbSelectOut_clicked(); + void on_lvUnselected_doubleClicked(const QModelIndex &index); + void on_lvSelected_doubleClicked(const QModelIndex &index); +}; + +#endif + diff --git a/client/portstatsmodel.cpp b/client/portstatsmodel.cpp new file mode 100644 index 0000000..1658980 --- /dev/null +++ b/client/portstatsmodel.cpp @@ -0,0 +1,326 @@ +/* +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 +*/ + +#include "portstatsmodel.h" +#include "portgrouplist.h" + +#include + +PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); + timer->start(1000); +} + +PortStatsModel::~PortStatsModel() +{ + timer->stop(); + delete timer; +} + +int PortStatsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (numPorts.isEmpty()) + return 0; + + if (numPorts.last() == 0) + return 0; + + return (int) e_STAT_MAX; +} + +int PortStatsModel::columnCount(const QModelIndex &parent ) const +{ + if (parent.isValid()) + return 0; + else + if (numPorts.isEmpty()) + return 0; + else + return numPorts.last(); +} + +void PortStatsModel::getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const +{ + int portNum; + + // TODO(LOW): Optimize using binary search: see qLowerBound() + portNum = index.column() + 1; + for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) + if (portNum <= numPorts.at(portGroupIdx)) + break; + + if (portGroupIdx) + { + if (numPorts.at(portGroupIdx -1)) + portIdx = (portNum - 1) - numPorts.at(portGroupIdx - 1); + else + portIdx = portNum - 1; + } + else + portIdx = portNum - 1; + + //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); +} + +QVariant PortStatsModel::data(const QModelIndex &index, int role) const +{ + uint pgidx, pidx; + int row; + + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + row = index.row(); + if (row >= e_STAT_MAX) + return QVariant(); + + if (numPorts.isEmpty()) + return QVariant(); + + if (index.column() >= (numPorts.last())) + return QVariant(); + + getDomainIndexes(index, pgidx, pidx); + + // Check role + if (role == Qt::DisplayRole) + { + OstProto::PortStats stats; + + stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); + + switch(row) + { + // States + case e_LINK_STATE: + return LinkStateName.at(stats.state().link_state()); + + case e_TRANSMIT_STATE: + return BoolStateName.at(stats.state().is_transmit_on()); + + case e_CAPTURE_STATE: + return BoolStateName.at(stats.state().is_capture_on()); + + // Statistics + case e_STAT_FRAMES_RCVD: + return quint64(stats.rx_pkts()); + + case e_STAT_FRAMES_SENT: + return quint64(stats.tx_pkts()); + + case e_STAT_FRAME_SEND_RATE: + return quint64(stats.tx_pps()); + + case e_STAT_FRAME_RECV_RATE: + return quint64(stats.rx_pps()); + + case e_STAT_BYTES_RCVD: + return quint64(stats.rx_bytes()); + + case e_STAT_BYTES_SENT: + return quint64(stats.tx_bytes()); + + case e_STAT_BYTE_SEND_RATE: + return quint64(stats.tx_bps()); + + case e_STAT_BYTE_RECV_RATE: + return quint64(stats.rx_bps()); + +#if 0 + case e_STAT_FRAMES_RCVD_NIC: + return stats.rx_pkts_nic(); + + case e_STAT_FRAMES_SENT_NIC: + return stats.tx_pkts_nic(); + + case e_STAT_BYTES_RCVD_NIC: + return stats.rx_bytes_nic(); + + case e_STAT_BYTES_SENT_NIC: + return stats.tx_bytes_nic(); +#endif + case e_STAT_RX_DROPS : return quint64(stats.rx_drops()); + case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); + case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); + case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors()); + + default: + qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, + index.row()); + return 0; + } + } + else if (role == Qt::TextAlignmentRole) + { + if (row >= e_STATE_START && row <= e_STATE_END) + return Qt::AlignHCenter; + else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) + return Qt::AlignRight; + else + return QVariant(); + } + else + return QVariant(); + +} + +QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::ToolTipRole) + { + if (orientation == Qt::Horizontal) + { + QString notes; + uint portGroupIdx, portIdx; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); + if (!notes.isEmpty()) + return notes; + else + return QVariant(); + } + else + return QVariant(); + } + + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + uint portGroupIdx, portIdx; + QString portName; + + if (numPorts.isEmpty() || section >= numPorts.last()) + return QVariant(); + getDomainIndexes(index(0, section), portGroupIdx, portIdx); + portName = QString("Port %1-%2") + .arg(pgl->mPortGroups.at(portGroupIdx)->id()) + .arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id()); + if (portGroupIdx < (uint) pgl->mPortGroups.size() + && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) + { + if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() + .isEmpty()) + portName += " *"; + } + return portName; + } + else + return PortStatName.at(section); +} + +void PortStatsModel::portListFromIndex(QModelIndexList indices, + QList &portList) +{ + int i, j; + QModelIndexList selectedCols(indices); + + portList.clear(); + + //selectedCols = indices.selectedColumns(); + for (i = 0; i < selectedCols.size(); i++) + { + uint portGroupIdx, portIdx; + + getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); + for (j = 0; j < portList.size(); j++) + { + if (portList[j].portGroupId == portGroupIdx) + break; + } + + if (j >= portList.size()) + { + // PortGroup Not found + PortGroupAndPortList p; + + p.portGroupId = portGroupIdx; + p.portList.append(portIdx); + + portList.append(p); + } + else + { + // PortGroup found + + portList[j].portList.append(portIdx); + } + } +} + +// +// Slots +// +void PortStatsModel::when_portListChanged() +{ + int i, count = 0; + + // recalc numPorts + while (numPorts.size()) + numPorts.removeFirst(); + + for (i = 0; i < pgl->mPortGroups.size(); i++) + { + count += pgl->mPortGroups.at(i)->numPorts(); + numPorts.append(count); + } + + reset(); +} + +void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) +{ + QModelIndex topLeft = index(port, 0, QModelIndex()); + QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} + +void PortStatsModel::updateStats() +{ + // Request each portgroup to fetch updated stats - the port group + // raises a signal once updated stats are available + for (int i = 0; i < pgl->mPortGroups.size(); i++) + pgl->mPortGroups[i]->getPortStats(); +} + +void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) +{ + // FIXME(MED): update only the changed ports, not all + + QModelIndex topLeft = index(0, 0, QModelIndex()); + QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex()); + + emit dataChanged(topLeft, bottomRight); +} diff --git a/client/portstatsmodel.h b/client/portstatsmodel.h new file mode 100644 index 0000000..a0d6868 --- /dev/null +++ b/client/portstatsmodel.h @@ -0,0 +1,151 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_MODEL_H +#define _PORT_STATS_MODEL_H + +#include +#include + +class QTimer; + +typedef enum { + // State + e_STATE_START = 0, + + e_LINK_STATE = e_STATE_START, + e_TRANSMIT_STATE, + e_CAPTURE_STATE, + + e_STATE_END = e_CAPTURE_STATE, + + // Statistics + e_STATISTICS_START, + + e_STAT_FRAMES_RCVD = e_STATISTICS_START, + e_STAT_FRAMES_SENT, + e_STAT_FRAME_SEND_RATE, + e_STAT_FRAME_RECV_RATE, + e_STAT_BYTES_RCVD, + e_STAT_BYTES_SENT, + e_STAT_BYTE_SEND_RATE, + e_STAT_BYTE_RECV_RATE, +#if 0 + e_STAT_FRAMES_RCVD_NIC, + e_STAT_FRAMES_SENT_NIC, + e_STAT_BYTES_RCVD_NIC, + e_STAT_BYTES_SENT_NIC, +#endif + + // Rx Errors + e_STAT_RX_DROPS, + e_STAT_RX_ERRORS, + e_STAT_RX_FIFO_ERRORS, + e_STAT_RX_FRAME_ERRORS, + + e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, + + e_STAT_MAX +} PortStat; + +static QStringList PortStatName = (QStringList() + << "Link State" + << "Transmit State" + << "Capture State" + + << "Frames Received" + << "Frames Sent" + << "Frame Send Rate (fps)" + << "Frame Receive Rate (fps)" + << "Bytes Received" + << "Bytes Sent" + << "Byte Send Rate (Bps)" + << "Byte Receive Rate (Bps)" +#if 0 + << "Frames Received (NIC)" + << "Frames Sent (NIC)" + << "Bytes Received (NIC)" + << "Bytes Sent (NIC)" +#endif + << "Receive Drops" + << "Receive Errors" + << "Receive Fifo Errors" + << "Receive Frame Errors" +); + +static QStringList LinkStateName = (QStringList() + << "Unknown" + << "Down" + << "Up" +); + +static QStringList BoolStateName = (QStringList() + << "Off" + << "On" +); + +class PortGroupList; + +class PortStatsModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + + PortStatsModel(PortGroupList *p, QObject *parent = 0); + ~PortStatsModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + class PortGroupAndPortList { + public: + uint portGroupId; + QList portList; + }; + void portListFromIndex(QModelIndexList indices, + QList &portList); + + public slots: + void when_portListChanged(); + void on_portStatsUpdate(int port, void*stats); + void when_portGroup_stats_update(quint32 portGroupId); + + private slots: + void updateStats(); + + private: + PortGroupList *pgl; + + // numPorts stores the num of ports per portgroup + // in the same order as the portgroups are index in the pgl + // Also it stores them as cumulative totals + QList numPorts; + + QTimer *timer; + + void getDomainIndexes(const QModelIndex &index, + uint &portGroupIdx, uint &portIdx) const; + +}; + +#endif diff --git a/client/portstatswindow.cpp b/client/portstatswindow.cpp new file mode 100644 index 0000000..d82a1fa --- /dev/null +++ b/client/portstatswindow.cpp @@ -0,0 +1,186 @@ +/* +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 +*/ + + +#include "portstatswindow.h" +#include "portstatsmodel.h" +#include "portstatsfilterdialog.h" + +#include "QHeaderView" + +PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + + this->pgl = pgl; + model = pgl->getPortStatsModel(); + tvPortStats->setModel(model); + + tvPortStats->verticalHeader()->setHighlightSections(false); + tvPortStats->verticalHeader()->setDefaultSectionSize( + tvPortStats->verticalHeader()->minimumSectionSize()); + +} + +PortStatsWindow::~PortStatsWindow() +{ +} + +/* ------------- SLOTS -------------- */ + +void PortStatsWindow::on_tbStartTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopTransmit_clicked() +{ + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopTx(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStartCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + startCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbStopCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + stopCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbViewCapture_clicked() +{ + // TODO(MED) + QList pgpl; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + pgpl); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < pgpl.size(); i++) + { + pgl->portGroupByIndex(pgpl.at(i).portGroupId). + viewCapture(&pgpl[i].portList); + } +} + +void PortStatsWindow::on_tbClear_clicked() +{ + QList portList; + + // Get selected ports + model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(), + portList); + + // Clear selected ports, portgroup by portgroup + for (int i = 0; i < portList.size(); i++) + { + pgl->portGroupByIndex(portList.at(i).portGroupId). + clearPortStats(&portList[i].portList); + } +} + +void PortStatsWindow::on_tbClearAll_clicked() +{ + for (int i = 0; i < pgl->numPortGroups(); i++) + { + pgl->portGroupByIndex(i).clearPortStats(); + } +} + +void PortStatsWindow::on_tbFilter_clicked() +{ + bool ok; + QList currentColumns, newColumns; + PortStatsFilterDialog dialog; + + // create the input list for the filter dialog - + // list of logical-indexes ordered by their current visual indexes + for(int vi = 0; vi < model->columnCount(); vi++) { + int li = tvPortStats->horizontalHeader()->logicalIndex(vi); + if (!tvPortStats->isColumnHidden(li)) { + currentColumns.append(li); + } + } + + // return list from the filter dialog - + // list of logical-indexes ordered by their new visual indexes + newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns); + + if (ok) + { + QHeaderView *hv = tvPortStats->horizontalHeader(); + + // hide/show sections first ... + for(int li = 0; li < model->columnCount(); li++) + tvPortStats->setColumnHidden(li, !newColumns.contains(li)); + + // ... then for the 'shown' columns, set the visual index + for(int vi = 0; vi < newColumns.size(); vi++) + hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi); + } +} diff --git a/client/portstatswindow.h b/client/portstatswindow.h new file mode 100644 index 0000000..39f9108 --- /dev/null +++ b/client/portstatswindow.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _PORT_STATS_WINDOW_H +#define _PORT_STATS_WINDOW_H + +#include +#include +#include "ui_portstatswindow.h" +#include "portgrouplist.h" +#include "portstatsmodel.h" + +class PortStatsWindow : public QWidget, public Ui::PortStatsWindow +{ + Q_OBJECT + +public: + PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortStatsWindow(); + +private: + PortGroupList *pgl; + PortStatsModel *model; + +private slots: + void on_tbStartTransmit_clicked(); + void on_tbStopTransmit_clicked(); + + void on_tbStartCapture_clicked(); + void on_tbStopCapture_clicked(); + void on_tbViewCapture_clicked(); + + void on_tbClear_clicked(); + void on_tbClearAll_clicked(); + + void on_tbFilter_clicked(); +}; + +#endif + diff --git a/client/portstatswindow.ui b/client/portstatswindow.ui new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/client/portstatswindow.ui @@ -0,0 +1,182 @@ + + PortStatsWindow + + + + 0 + 0 + 502 + 415 + + + + Form + + + + + + QFrame::Panel + + + QFrame::Raised + + + + + + Start Tx + + + Starts transmit on selected port(s) + + + Start Transmit + + + :/icons/control_play.png + + + + + + + Stop Tx + + + Stops transmit on selected port(s) + + + Stop Trasmit + + + :/icons/control_stop.png + + + + + + + Clear Selected Port Stats + + + Clears statistics of the selected port(s) + + + Clear + + + :/icons/portstats_clear.png + + + + + + + Clear All Ports Stats + + + Clears statistics of all ports + + + Clear All + + + :/icons/portstats_clear_all.png + + + + + + + Start Capture + + + Captures packets on the selected port(s) + + + Start Capture + + + :/icons/sound_none.png + + + + + + + Stop Capture + + + End capture on selecteed port(s) + + + Stop Capture + + + :/icons/sound_mute.png + + + + + + + View Capture Buffer + + + View captured packets on selected port(s) + + + View Capture + + + :/icons/magnifier.png + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Select which ports to view + + + Filter + + + :/icons/portstats_filter.png + + + + + + + + + + + + + + + + diff --git a/client/portswindow.cpp b/client/portswindow.cpp new file mode 100644 index 0000000..1f3bc4d --- /dev/null +++ b/client/portswindow.cpp @@ -0,0 +1,663 @@ +/* +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 +*/ + +#include "portswindow.h" + +#include "abstractfileformat.h" +#include "portconfigdialog.h" +#include "streamconfigdialog.h" +#include "streamlistdelegate.h" + +#include +#include +#include +#include + +PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) + : QWidget(parent) +{ + QAction *sep; + + delegate = new StreamListDelegate; + //slm = new StreamListModel(); + //plm = new PortGroupList(); + plm = pgl; + + setupUi(this); + + tvPortList->header()->hide(); + + tvStreamList->setItemDelegate(delegate); + + tvStreamList->verticalHeader()->setDefaultSectionSize( + tvStreamList->verticalHeader()->minimumSectionSize()); + + // Populate PortList Context Menu Actions + tvPortList->addAction(actionNew_Port_Group); + tvPortList->addAction(actionDelete_Port_Group); + tvPortList->addAction(actionConnect_Port_Group); + tvPortList->addAction(actionDisconnect_Port_Group); + + tvPortList->addAction(actionExclusive_Control); + tvPortList->addAction(actionPort_Configuration); + + // Populate StramList Context Menu Actions + tvStreamList->addAction(actionNew_Stream); + tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDelete_Stream); + + sep = new QAction(this); + sep->setSeparator(true); + tvStreamList->addAction(sep); + + tvStreamList->addAction(actionOpen_Streams); + tvStreamList->addAction(actionSave_Streams); + + // PortList and StreamList actions combined make this window's actions + addActions(tvPortList->actions()); + sep = new QAction(this); + sep->setSeparator(true); + addAction(sep); + addActions(tvStreamList->actions()); + + tvStreamList->setModel(plm->getStreamModel()); + tvPortList->setModel(plm->getPortModel()); + + connect( plm->getPortModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portModel_dataChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getPortModel(), SIGNAL(modelReset()), + SLOT(when_portModel_reset())); + + connect( tvPortList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_portView_currentChanged(const QModelIndex&, + const QModelIndex&))); + + connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + SLOT(updateStreamViewActions())); + + connect(tvStreamList->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateStreamViewActions())); + connect(tvStreamList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SLOT(updateStreamViewActions())); + + tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); + tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); + + // Initially we don't have any ports/streams - so send signal triggers + when_portView_currentChanged(QModelIndex(), QModelIndex()); + updateStreamViewActions(); + + connect(plm->getStreamModel(), + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(streamModelDataChanged())); + connect(plm->getStreamModel(), + SIGNAL(modelReset()), + this, SLOT(streamModelDataChanged())); +} + +void PortsWindow::streamModelDataChanged() +{ + if (plm->isPort(tvPortList->currentIndex())) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); +} + +PortsWindow::~PortsWindow() +{ + delete delegate; +} + +void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) +{ + StreamConfigDialog *scd; + int ret; + + if (!index.isValid()) + { + qDebug("%s: invalid index", __FUNCTION__); + return; + } + scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()), + index.row(), this); + qDebug("stream list activated\n"); + ret = scd->exec(); + + if (ret == QDialog::Accepted) + plm->port(tvPortList->currentIndex()).recalculateAverageRates(); + + delete scd; +} + +void PortsWindow::when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous) +{ + plm->getStreamModel()->setCurrentPortIndex(current); + updatePortViewActions(current); + updateStreamViewActions(); + + qDebug("In %s", __FUNCTION__); + + if (previous.isValid() && plm->isPort(previous)) + { + disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), + this, SLOT(updatePortRates())); + } + + if (!current.isValid()) + { + qDebug("setting stacked widget to blank page"); + swDetail->setCurrentIndex(2); // blank page + } + else + { + if (plm->isPortGroup(current)) + { + swDetail->setCurrentIndex(1); // portGroup detail page + } + else if (plm->isPort(current)) + { + swDetail->setCurrentIndex(0); // port detail page + updatePortRates(); + connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), + SLOT(updatePortRates())); + } + } +} + +void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + qDebug("In %s", __FUNCTION__); +#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex + if ((tvPortList->currentIndex() >= topLeft) && + (tvPortList->currentIndex() <= bottomRight)) +#endif + if (((topLeft < tvPortList->currentIndex()) || + (topLeft == tvPortList->currentIndex())) && + (((tvPortList->currentIndex() < bottomRight)) || + (tvPortList->currentIndex() == bottomRight))) + { + // Update UI to reflect potential change in exclusive mode, + // transmit mode et al + when_portView_currentChanged(tvPortList->currentIndex(), + tvPortList->currentIndex()); + } +} + +void PortsWindow::when_portModel_reset() +{ + when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); +} + +void PortsWindow::on_averagePacketsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); + + plm->port(current).setAveragePacketRate(pps); +} + +void PortsWindow::on_averageBitsPerSec_editingFinished() +{ + QModelIndex current = tvPortList->currentIndex(); + + Q_ASSERT(plm->isPort(current)); + + bool isOk; + double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); + + plm->port(current).setAverageBitRate(bps); +} + +void PortsWindow::updatePortRates() +{ + QModelIndex current = tvPortList->currentIndex(); + + if (!current.isValid()) + return; + + if (!plm->isPort(current)) + return; + + averagePacketsPerSec->setText(QString("%L1") + .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); + averageBitsPerSec->setText(QString("%L1") + .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); +} + +void PortsWindow::updateStreamViewActions() +{ + // For some reason hasSelection() returns true even if selection size is 0 + // so additional check for size introduced + if (tvStreamList->selectionModel()->hasSelection() && + (tvStreamList->selectionModel()->selection().size() > 0)) + { + qDebug("Has selection %d", + tvStreamList->selectionModel()->selection().size()); + + // If more than one non-contiguous ranges selected, + // disable "New" and "Edit" + if (tvStreamList->selectionModel()->selection().size() > 1) + { + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + } + else + { + actionNew_Stream->setEnabled(true); + + // Enable "Edit" only if the single range has a single row + if (tvStreamList->selectionModel()->selection().at(0).height() > 1) + actionEdit_Stream->setDisabled(true); + else + actionEdit_Stream->setEnabled(true); + } + + // Delete is always enabled as long as we have a selection + actionDelete_Stream->setEnabled(true); + } + else + { + qDebug("No selection"); + if (plm->isPort(tvPortList->currentIndex())) + actionNew_Stream->setEnabled(true); + else + actionNew_Stream->setDisabled(true); + actionEdit_Stream->setDisabled(true); + actionDelete_Stream->setDisabled(true); + } + actionOpen_Streams->setEnabled(plm->isPort( + tvPortList->selectionModel()->currentIndex())); + actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); +} + +void PortsWindow::updatePortViewActions(const QModelIndex& current) +{ + if (!current.isValid()) + { + qDebug("current is now invalid"); + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + goto _EXIT; + } + + qDebug("currentChanged %llx", current.internalId()); + + if (plm->isPortGroup(current)) + { + actionDelete_Port_Group->setEnabled(true); + + actionExclusive_Control->setDisabled(true); + actionPort_Configuration->setDisabled(true); + + switch(plm->portGroup(current).state()) + { + case QAbstractSocket::UnconnectedState: + case QAbstractSocket::ClosingState: + qDebug("state = unconnected|closing"); + actionConnect_Port_Group->setEnabled(true); + actionDisconnect_Port_Group->setDisabled(true); + break; + + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::ConnectedState: + qDebug("state = lookup|connecting|connected"); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setEnabled(true); + break; + + + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + default: + // FIXME(LOW): indicate error + qDebug("unexpected state"); + break; + } + } + else if (plm->isPort(current)) + { + actionDelete_Port_Group->setDisabled(true); + actionConnect_Port_Group->setDisabled(true); + actionDisconnect_Port_Group->setDisabled(true); + + actionExclusive_Control->setEnabled(true); + if (plm->port(current).hasExclusiveControl()) + actionExclusive_Control->setChecked(true); + else + actionExclusive_Control->setChecked(false); + actionPort_Configuration->setEnabled(true); + } + +_EXIT: + return; +} + +void PortsWindow::on_pbApply_clicked() +{ + QModelIndex curPort; + QModelIndex curPortGroup; + + curPort = tvPortList->selectionModel()->currentIndex(); + if (!curPort.isValid()) + { + qDebug("%s: curPort is invalid", __FUNCTION__); + goto _exit; + } + + if (!plm->isPort(curPort)) + { + qDebug("%s: curPort is not a port", __FUNCTION__); + goto _exit; + } + + if (plm->port(curPort).getStats().state().is_transmit_on()) + { + QMessageBox::information(0, "Configuration Change", + "Please stop transmit on the port before applying any changes"); + goto _exit; + } + + curPortGroup = plm->getPortModel()->parent(curPort); + if (!curPortGroup.isValid()) + { + qDebug("%s: curPortGroup is invalid", __FUNCTION__); + goto _exit; + } + if (!plm->isPortGroup(curPortGroup)) + { + qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); + goto _exit; + } + + // FIXME(HI): shd this be a signal? + //portGroup.when_configApply(port); + // FIXME(MED): mixing port id and index!!! + plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); + +_exit: + return; + +#if 0 + // TODO (LOW): This block is for testing only + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + qDebug("current = %llx", current.internalId()); + else + qDebug("current is invalid"); +#endif +} + +void PortsWindow::on_actionNew_Port_Group_triggered() +{ + bool ok; + QString text = QInputDialog::getText(this, + "Add Port Group", "Port Group Address (IP[:Port])", + QLineEdit::Normal, lastNewPortGroup, &ok); + + if (ok) + { + QStringList addr = text.split(":"); + if (addr.size() == 1) // Port unspecified + addr.append(QString().setNum(DEFAULT_SERVER_PORT)); + PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); + plm->addPortGroup(*pg); + lastNewPortGroup = text; + } +} + +void PortsWindow::on_actionDelete_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->removePortGroup(plm->portGroup(current)); +} + +void PortsWindow::on_actionConnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).connectToHost(); +} + +void PortsWindow::on_actionDisconnect_Port_Group_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (current.isValid()) + plm->portGroup(current).disconnectFromHost(); +} + +void PortsWindow::on_actionExclusive_Control_triggered(bool checked) +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (plm->isPort(current)) + { + OstProto::Port config; + + config.set_is_exclusive_control(checked); + plm->portGroup(current.parent()).modifyPort(current.row(), config); + } +} + +void PortsWindow::on_actionPort_Configuration_triggered() +{ + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + + if (!plm->isPort(current)) + return; + + OstProto::Port config; + config.set_transmit_mode(plm->port(current).transmitMode()); + config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); + + PortConfigDialog dialog(config, this); + + if (dialog.exec() == QDialog::Accepted) + plm->portGroup(current.parent()).modifyPort(current.row(), config); +} + +void PortsWindow::on_actionNew_Stream_triggered() +{ + qDebug("New Stream Action"); + + // In case nothing is selected, insert 1 row at the top + int row = 0, count = 1; + + // In case we have a single range selected; insert as many rows as + // in the singe selected range before the top of the selected range + if (tvStreamList->selectionModel()->selection().size() == 1) + { + row = tvStreamList->selectionModel()->selection().at(0).top(); + count = tvStreamList->selectionModel()->selection().at(0).height(); + } + + plm->getStreamModel()->insertRows(row, count); +} + +void PortsWindow::on_actionEdit_Stream_triggered() +{ + qDebug("Edit Stream Action"); + + // Ensure we have only one range selected which contains only one row + if ((tvStreamList->selectionModel()->selection().size() == 1) && + (tvStreamList->selectionModel()->selection().at(0).height() == 1)) + { + on_tvStreamList_activated(tvStreamList->selectionModel()-> + selection().at(0).topLeft()); + } +} + +void PortsWindow::on_actionDelete_Stream_triggered() +{ + qDebug("Delete Stream Action"); + + QModelIndex index; + + if (tvStreamList->selectionModel()->hasSelection()) + { + qDebug("SelectedIndexes %d", + tvStreamList->selectionModel()->selectedRows().size()); + while(tvStreamList->selectionModel()->selectedRows().size()) + { + index = tvStreamList->selectionModel()->selectedRows().at(0); + plm->getStreamModel()->removeRows(index.row(), 1); + } + } + else + qDebug("No selection"); +} + +void PortsWindow::on_actionOpen_Streams_triggered() +{ + qDebug("Open Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString dirName; + QString fileName; + QString errorStr; + bool append = true; + bool ret; + + Q_ASSERT(plm->isPort(current)); + + fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); + if (fileName.isEmpty()) + goto _exit; + + if (tvStreamList->model()->rowCount()) + { + QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), + tr("Append to existing streams? Or overwrite?"), + QMessageBox::NoButton, this); + QPushButton *appendBtn = msgBox.addButton(tr("Append"), + QMessageBox::ActionRole); + QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), + QMessageBox::ActionRole); + QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); + + msgBox.exec(); + + if (msgBox.clickedButton() == cancelBtn) + goto _exit; + else if (msgBox.clickedButton() == appendBtn) + append = true; + else if (msgBox.clickedButton() == overwriteBtn) + append = false; + else + Q_ASSERT(false); + } + + ret = plm->port(current).openStreams(fileName, append, errorStr); + if (!ret || !errorStr.isEmpty()) + { + QMessageBox msgBox(this); + QStringList str = errorStr.split("\n\n\n\n"); + + msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); + msgBox.setWindowTitle(qApp->applicationName()); + msgBox.setText(str.at(0)); + if (str.size() > 1) + msgBox.setDetailedText(str.at(1)); + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + dirName = QFileInfo(fileName).absolutePath(); + +_exit: + return; +} + +void PortsWindow::on_actionSave_Streams_triggered() +{ + qDebug("Save Streams Action"); + + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + static QString fileName; + QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); + QString fileType; + QString errorStr; + QFileDialog::Options options; + + // On Mac OS with Native Dialog, getSaveFileName() ignores fileType + // which we need.On some Linux distros the native dialog can't + // distinguish between Ostinato(*) and PCAP(*) +#if defined(Q_OS_MAC) || defined(Q_OS_UNIX) + options |= QFileDialog::DontUseNativeDialog; +#endif + + if (fileTypes.size()) + fileType = fileTypes.at(0); + + Q_ASSERT(plm->isPort(current)); + +_retry: + fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), + fileName, fileTypes.join(";;"), &fileType, options); + if (fileName.isEmpty()) + goto _exit; + + fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); + if (!fileType.startsWith("Ostinato")) + { + if (QMessageBox::warning(this, tr("Ostinato"), + QString("You have chosen to save in %1 format. All stream " + "attributes may not be saved in this format.\n\n" + "It is recommended to save in native Ostinato format.\n\n" + "Continue to save in %2 format?").arg(fileType).arg(fileType), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) + goto _retry; + } + + // TODO: all or selected? + + if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) + QMessageBox::critical(this, qApp->applicationName(), errorStr); + else if (!errorStr.isEmpty()) + QMessageBox::warning(this, qApp->applicationName(), errorStr); + + fileName = QFileInfo(fileName).absolutePath(); +_exit: + return; +} + + diff --git a/client/portswindow.h b/client/portswindow.h new file mode 100644 index 0000000..5c071f0 --- /dev/null +++ b/client/portswindow.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _PORTS_WINDOW_H +#define _PORTS_WINDOW_H + +#include +#include +#include "ui_portswindow.h" +#include "portgrouplist.h" + +/* TODO +HIGH +MED +LOW +*/ + +class QAbstractItemDelegate; + +class PortsWindow : public QWidget, private Ui::PortsWindow +{ + Q_OBJECT + + //QAbstractItemModel *slm; // stream list model + PortGroupList *plm; + +public: + PortsWindow(PortGroupList *pgl, QWidget *parent = 0); + ~PortsWindow(); + +private: + QString lastNewPortGroup; + QAbstractItemDelegate *delegate; + +private slots: + void updatePortViewActions(const QModelIndex& current); + void updateStreamViewActions(); + + void on_averagePacketsPerSec_editingFinished(); + void on_averageBitsPerSec_editingFinished(); + void updatePortRates(); + void on_tvStreamList_activated(const QModelIndex & index); + void when_portView_currentChanged(const QModelIndex& current, + const QModelIndex& previous); + void when_portModel_dataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight); + void when_portModel_reset(); + + void on_pbApply_clicked(); + + void on_actionNew_Port_Group_triggered(); + void on_actionDelete_Port_Group_triggered(); + void on_actionConnect_Port_Group_triggered(); + void on_actionDisconnect_Port_Group_triggered(); + + void on_actionExclusive_Control_triggered(bool checked); + void on_actionPort_Configuration_triggered(); + + void on_actionNew_Stream_triggered(); + void on_actionEdit_Stream_triggered(); + void on_actionDelete_Stream_triggered(); + + void on_actionOpen_Streams_triggered(); + void on_actionSave_Streams_triggered(); + + void streamModelDataChanged(); +}; + +#endif + diff --git a/client/portswindow.ui b/client/portswindow.ui new file mode 100644 index 0000000..a5d9261 --- /dev/null +++ b/client/portswindow.ui @@ -0,0 +1,299 @@ + + PortsWindow + + + + 0 + 0 + 710 + 352 + + + + Form + + + + + + Qt::Horizontal + + + false + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Avg pps + + + true + + + + + + + + + + Avg bps + + + + + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 1 + + + + Qt::ActionsContextMenu + + + QFrame::StyledPanel + + + 1 + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Select a port to configure streams + + + Qt::AlignCenter + + + + + + + + + + + + + :/icons/portgroup_add.png + + + New Port Group + + + + + :/icons/portgroup_delete.png + + + Delete Port Group + + + + + :/icons/portgroup_connect.png + + + Connect Port Group + + + + + :/icons/portgroup_disconnect.png + + + Disconnect Port Group + + + + + :/icons/stream_add.png + + + New Stream + + + + + :/icons/stream_delete.png + + + Delete Stream + + + + + :/icons/stream_edit.png + + + Edit Stream + + + + + true + + + Exclusive Port Control (EXPERIMENTAL) + + + + + Open Streams ... + + + + + Save Streams ... + + + + + Port Configuration ... + + + + + + + + + radioButton + toggled(bool) + averagePacketsPerSec + setEnabled(bool) + + + 313 + 28 + + + 380 + 28 + + + + + radioButton_2 + toggled(bool) + averageBitsPerSec + setEnabled(bool) + + + 333 + 55 + + + 395 + 56 + + + + + diff --git a/client/preferences.cpp b/client/preferences.cpp new file mode 100644 index 0000000..0e54bbd --- /dev/null +++ b/client/preferences.cpp @@ -0,0 +1,119 @@ +/* +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 +*/ + +#include "preferences.h" + +#include "../common/ostprotolib.h" +#include "settings.h" + +#include + +Preferences::Preferences() +{ + Q_ASSERT(appSettings); + + setupUi(this); + + wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, + kWiresharkPathDefaultValue).toString()); + tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, + kTsharkPathDefaultValue).toString()); + gzipPathEdit->setText(appSettings->value(kGzipPathKey, + kGzipPathDefaultValue).toString()); + diffPathEdit->setText(appSettings->value(kDiffPathKey, + kDiffPathDefaultValue).toString()); + awkPathEdit->setText(appSettings->value(kAwkPathKey, + kAwkPathDefaultValue).toString()); +} + +Preferences::~Preferences() +{ +} + +void Preferences::accept() +{ + appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); + appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); + appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); + appSettings->setValue(kDiffPathKey, diffPathEdit->text()); + appSettings->setValue(kAwkPathKey, awkPathEdit->text()); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + QDialog::accept(); +} + +void Preferences::on_wiresharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate Wireshark", + wiresharkPathEdit->text()); + + if (!path.isEmpty()) + wiresharkPathEdit->setText(path); +} + +void Preferences::on_tsharkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate tshark", + tsharkPathEdit->text()); + + if (!path.isEmpty()) + tsharkPathEdit->setText(path); +} + +void Preferences::on_gzipPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate gzip", + gzipPathEdit->text()); + + if (!path.isEmpty()) + gzipPathEdit->setText(path); +} + +void Preferences::on_diffPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate diff", + diffPathEdit->text()); + + if (!path.isEmpty()) + diffPathEdit->setText(path); +} + +void Preferences::on_awkPathButton_clicked() +{ + QString path; + + path = QFileDialog::getOpenFileName(0, "Locate awk", + awkPathEdit->text()); + + if (!path.isEmpty()) + awkPathEdit->setText(path); +} diff --git a/client/preferences.h b/client/preferences.h new file mode 100644 index 0000000..78109ab --- /dev/null +++ b/client/preferences.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PREFERENCES_H +#define _PREFERENCES_H + +#include "ui_preferences.h" + +#include + +class Preferences : public QDialog, private Ui::Preferences +{ + Q_OBJECT +public: + Preferences(); + ~Preferences(); + +public slots: + void accept(); + +private slots: + void on_wiresharkPathButton_clicked(); + void on_tsharkPathButton_clicked(); + void on_gzipPathButton_clicked(); + void on_diffPathButton_clicked(); + void on_awkPathButton_clicked(); +}; + +#endif diff --git a/client/preferences.ui b/client/preferences.ui new file mode 100644 index 0000000..d64b4cb --- /dev/null +++ b/client/preferences.ui @@ -0,0 +1,226 @@ + + Preferences + + + + 0 + 0 + 400 + 220 + + + + Preferences + + + :/icons/preferences.png + + + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 'wireshark' Path + + + wiresharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'tshark' Path + + + tsharkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'gzip' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'diff' Path + + + diffPathEdit + + + + + + + false + + + + + + + ... + + + + + + + 'awk' Path + + + awkPathEdit + + + + + + + false + + + + + + + ... + + + + + + + Qt::Vertical + + + + 21 + 61 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + wiresharkPathEdit + wiresharkPathButton + tsharkPathEdit + tsharkPathButton + gzipPathEdit + gzipPathButton + diffPathEdit + diffPathButton + awkPathEdit + awkPathButton + buttonBox + + + + + + + buttonBox + accepted() + Preferences + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Preferences + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 0000000..d76bb47 --- /dev/null +++ b/client/settings.h @@ -0,0 +1,86 @@ +/* +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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +const QString kWiresharkPathKey("WiresharkPath"); +#if defined(Q_OS_WIN32) +const QString kWiresharkPathDefaultValue( + "C:/Program Files/Wireshark/wireshark.exe"); +#elif defined(Q_OS_MAC) +const QString kWiresharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); +#else +const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); +#endif + +const QString kTsharkPathKey("TsharkPath"); +#if defined(Q_OS_WIN32) +const QString kTsharkPathDefaultValue( + "C:/Program Files/Wireshark/tshark.exe"); +#elif defined(Q_OS_MAC) +const QString kTsharkPathDefaultValue( + "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); +#else +const QString kTsharkPathDefaultValue("/usr/bin/tshark"); +#endif + +const QString kGzipPathKey("GzipPath"); +#if defined(Q_OS_WIN32) +extern QString kGzipPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#else +const QString kGzipPathDefaultValue("/usr/bin/gzip"); +#endif + +const QString kDiffPathKey("DiffPath"); +#if defined(Q_OS_WIN32) +extern QString kDiffPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#else +const QString kDiffPathDefaultValue("/usr/bin/diff"); +#endif + +const QString kAwkPathKey("AwkPath"); +#if defined(Q_OS_WIN32) +extern QString kAwkPathDefaultValue; +#elif defined(Q_OS_MAC) +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#else +const QString kAwkPathDefaultValue("/usr/bin/awk"); +#endif + + +// +// LastUse Section Keys +// +const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); +const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); + +#endif + + diff --git a/client/stream.cpp b/client/stream.cpp new file mode 100644 index 0000000..2305930 --- /dev/null +++ b/client/stream.cpp @@ -0,0 +1,51 @@ +/* +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 +*/ + +/*! + * \todo Remove this class + */ + +#include +#include + +#include "stream.h" +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" + +Stream::Stream() +{ + //mId = 0xFFFFFFFF; + setEnabled(true); +} + +Stream::~Stream() +{ +} + +void Stream::loadProtocolWidgets() +{ + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; +} + +void Stream::storeProtocolWidgets() +{ + qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); + return; +} diff --git a/client/stream.h b/client/stream.h new file mode 100644 index 0000000..213af70 --- /dev/null +++ b/client/stream.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include + +#include "../common/protocol.pb.h" +#include "../common/streambase.h" + +class Stream : public StreamBase { + + //quint32 mId; + +public: + Stream(); + ~Stream(); + + void loadProtocolWidgets(); + void storeProtocolWidgets(); +}; + +#endif diff --git a/client/streamconfigdialog.cpp b/client/streamconfigdialog.cpp new file mode 100644 index 0000000..32a4ca7 --- /dev/null +++ b/client/streamconfigdialog.cpp @@ -0,0 +1,1212 @@ +/* +Copyright (C) 2010-2011 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 + +#include "streamconfigdialog.h" +#include "stream.h" +#include "abstractprotocol.h" +#include "abstractprotocolconfig.h" +#include "protocollistiterator.h" + +#include "modeltest.h" + +#include "../common/protocolmanager.h" +#include "../common/protocolwidgetfactory.h" + +extern ProtocolManager *OstProtocolManager; +extern ProtocolWidgetFactory *OstProtocolWidgetFactory; + +QRect StreamConfigDialog::lastGeometry; +int StreamConfigDialog::lastTopLevelTabIndex = 0; +int StreamConfigDialog::lastProtocolDataIndex = 0; + +static const uint kEthFrameOverHead = 20; + +StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex, + QWidget *parent) : QDialog (parent), mPort(port) +{ + OstProto::Stream s; + mCurrentStreamIndex = streamIndex; + mpStream = new Stream; + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); + mpStream->protoDataCopyFrom(s); + _iter = mpStream->createProtocolListIterator(); + isUpdateInProgress = false; + + setupUi(this); + setupUiExtra(); + + for (int i = ProtoMin; i < ProtoMax; i++) + { + bgProto[i]->setProperty("ProtocolLevel", i); + bgProto[i]->setProperty("ProtocolId", ButtonIdNone); + connect(bgProto[i], SIGNAL(buttonClicked(int)), + this, SLOT(updateProtocol(int))); + } + + //! \todo causes a crash! +#if 0 + connect(lePktLen, SIGNAL(textEdited(QString)), + this, SLOT(updateContents())); +#endif + + // Time to play match the signals and slots! + + // If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None + connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); + + // If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and + // disable the subsequent protocol group as well + connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); + connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); + connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); + connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); + connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); + + // Setup valid subsequent protocols for L2 to L4 protocols + for (int i = ProtoL2; i <= ProtoL4; i++) + { + foreach(QAbstractButton *btn1, bgProto[i]->buttons()) + { + int id1 = bgProto[i]->id(btn1); + + if (id1 != ButtonIdNone && id1 != ButtonIdOther) + { + int validProtocolCount = 0; + + foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) + { + int id2 = bgProto[i+1]->id(btn2); + + if (id2 != ButtonIdNone && id2 != ButtonIdOther) + { + if (OstProtocolManager->isValidNeighbour(id1, id2)) + { + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setEnabled(bool))); + validProtocolCount++; + } + else + connect(btn1, SIGNAL(toggled(bool)), + btn2, SLOT(setDisabled(bool))); + } + } + + // If btn1 has no subsequent valid protocols, + // force subsequent Protocol to 'None' + if (validProtocolCount == 0) + connect(btn1, SIGNAL(clicked(bool)), + bgProto[i+1]->button(ButtonIdNone), SLOT(click())); + + // If the id1 protocol doesn't have a payload (e.g. IGMP) + // force payload protocol to 'None' + if (!OstProtocolManager->protocolHasPayload(id1)) + { + connect(btn1, SIGNAL(clicked(bool)), + bgProto[ProtoPayload]->button(ButtonIdNone), + SLOT(click())); + } + } + } + } + + mpAvailableProtocolsModel = new QStringListModel( + OstProtocolManager->protocolDatabase(), this); + lvAllProtocols->setModel(mpAvailableProtocolsModel); + lvAllProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); + mpSelectedProtocolsModel = new QStringListModel(this); + lvSelectedProtocols->setModel(mpSelectedProtocolsModel); + lvSelectedProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); + + + connect(lvAllProtocols->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(when_lvAllProtocols_selectionChanged( + const QItemSelection&, const QItemSelection&))); + connect(lvSelectedProtocols->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, + const QModelIndex&))); + + LoadCurrentStream(); + mpPacketModel = new PacketModel(this); + tvPacketTree->setModel(mpPacketModel); +#ifdef QT_NO_DEBUG + mpPacketModelTester = NULL; +#else + mpPacketModelTester = new ModelTest(mpPacketModel); +#endif + tvPacketTree->header()->hide(); + vwPacketDump->setModel(mpPacketModel); + vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); + + // TODO(MED): + //! \todo Enable navigation of streams + pbPrev->setHidden(true); + pbNext->setHidden(true); + //! \todo Support Goto Stream Id + leStreamId->setHidden(true); + disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); + + switch(mPort.transmitMode()) + { + case OstProto::kSequentialTransmit: + rbModeFixed->setChecked(true); + rbModeContinuous->setDisabled(true); + break; + case OstProto::kInterleavedTransmit: + rbModeContinuous->setChecked(true); + rbModeFixed->setDisabled(true); + + nextWhat->setHidden(true); + break; + default: + Q_ASSERT(false); // Unreachable + } + + // Finally, restore the saved last geometry and selected tab for the + // various tab widgets + if (!lastGeometry.isNull()) + setGeometry(lastGeometry); + twTopLevel->setCurrentIndex(lastTopLevelTabIndex); +} + +void StreamConfigDialog::setupUiExtra() +{ + QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); + QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + // ---- Setup default stuff that cannot be done in designer ---- + bgProto[ProtoL1] = new QButtonGroup(); + bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); + bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); + bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); + + bgProto[ProtoL2] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbFrameType->findChildren()) + bgL2Proto->addButton(btn); +#else + bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); + bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); + bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); + bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); + bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); +#endif + + bgProto[ProtoVlan] = new QButtonGroup(); + bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); + bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); + bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); + + bgProto[ProtoL3] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL3Proto->findChildren()) + bgProto[ProtoL3]->addButton(btn); +#else + bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); + bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over4, + OstProto::Protocol::kIp6over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over6, + OstProto::Protocol::kIp4over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip4over4, + OstProto::Protocol::kIp4over4FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Ip6over6, + OstProto::Protocol::kIp6over6FieldNumber); + bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); +#endif + + bgProto[ProtoL4] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL4Proto->findChildren()) + bgProto[ProtoL4]->addButton(btn); +#else + bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); + bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); + bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); +#endif + + bgProto[ProtoL5] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbL5Proto->findChildren()) + bgProto[ProtoL5]->addButton(btn); +#else + bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); + bgProto[ProtoL5]->addButton(rbL5Text, + OstProto::Protocol::kTextProtocolFieldNumber); + bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); +#endif + + bgProto[ProtoPayload] = new QButtonGroup(); +#if 0 + foreach(QRadioButton *btn, gbPayloadProto->findChildren()) + bgProto[ProtoPayload]->addButton(btn); +#else + bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); + bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); + bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); +#endif + /* + ** Setup Validators + */ + // Meta Data + lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); + lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); + + lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); + + /* + ** Setup Connections + */ + connect(rbSendPackets, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbSendBursts, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeFixed, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + connect(rbModeContinuous, SIGNAL(toggled(bool)), + this, SLOT(update_NumPacketsAndNumBursts())); + +} + +StreamConfigDialog::~StreamConfigDialog() +{ + delete mpPacketModelTester; + delete mpPacketModel; + + for (int i = ProtoMin; i < ProtoMax; i++) + delete bgProto[i]; + + foreach (AbstractProtocolConfigForm* w, _protocolWidgets) { + OstProtocolWidgetFactory->deleteConfigWidget(w); + } + + delete _iter; + delete mpStream; +} + +void StreamConfigDialog::loadProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: Protocol Widgets are created on demand. Once created we + // store them in _protocolWidgets indexed by the protocol + // object's address (to ensure unique widgets for multiple + // objects of the same class). Subsequently we check + // _protocolWidgets before creating a new widget + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + _protocolWidgets.insert(p, w); + } + w->loadWidget(p); + } + delete iter; +} + +void StreamConfigDialog::storeProtocolWidgets() +{ + ProtocolListIterator *iter; + + // NOTE: After creating a widget, we need to call loadWidget() + // to load the protocol's default values + iter = mpStream->createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol* p = iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + w->storeWidget(p); + } + delete iter; +} + +void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) +{ + if (mode == "Fixed") + { + lePktLen->setEnabled(true); + lePktLenMin->setDisabled(true); + lePktLenMax->setDisabled(true); + } + else if (mode == "Increment") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Decrement") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else if (mode == "Random") + { + lePktLen->setDisabled(true); + lePktLenMin->setEnabled(true); + lePktLenMax->setEnabled(true); + } + else + { + qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); + } +} + +void StreamConfigDialog::on_pbPrev_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx--; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_pbNext_clicked() +{ +#if 0 + StoreCurrentStream(currStreamIdx); + currStreamIdx++; + LoadCurrentStream(currStreamIdx); + + pbPrev->setDisabled((currStreamIdx == 0)); + pbNext->setDisabled((currStreamIdx == 2)); +#endif +} + +void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) +{ + qDebug("%s, index = %d", __FUNCTION__, index); + switch (index) + { + case 0: + updateSelectProtocolsSimpleWidget(); + break; + case 1: + updateSelectProtocolsAdvancedWidget(); + break; + default: + qFatal("%s: unexpected index = %d", __FUNCTION__, index); + } +} + +void StreamConfigDialog::when_lvAllProtocols_selectionChanged( + const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); + + qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); + + tbAdd->setEnabled(size > 0); +} + +void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); + + tbDelete->setEnabled(current.isValid()); + tbUp->setEnabled(current.isValid() && (current.row() != 0)); + tbDown->setEnabled(current.isValid() && + (current.row() != (current.model()->rowCount() - 1))); +} + +void StreamConfigDialog::on_tbAdd_clicked() +{ + int n = 0; + QModelIndex idx2; + QModelIndexList selection; + + selection = lvAllProtocols->selectionModel()->selectedIndexes(); + + // Validation + if (selection.size() == 0) + return; + + idx2 = lvSelectedProtocols->currentIndex(); + if (idx2.isValid()) + n = idx2.row(); + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + _iter->next(); + } + + foreach(QModelIndex idx, selection) + _iter->insert(OstProtocolManager->createProtocol( + mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx2); +} + +void StreamConfigDialog::on_tbDelete_clicked() +{ + int n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid()) + return; + + n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); + delete p; + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx); +} + +void StreamConfigDialog::on_tbUp_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == 0) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->previous(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); +} + +void StreamConfigDialog::on_tbDown_clicked() +{ + int m, n; + QModelIndex idx; + AbstractProtocol *p = NULL; + + idx = lvSelectedProtocols->currentIndex(); + + // Validation + if (!idx.isValid() || idx.row() == idx.model()->rowCount()) + return; + + m = n = idx.row() + 1; + + _iter->toFront(); + while (n--) + { + if (!_iter->hasNext()) + return; + + p = _iter->next(); + } + + Q_CHECK_PTR(p); + _iter->remove(); + _iter->next(); + _iter->insert(p); + + updateSelectProtocolsAdvancedWidget(); + lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); +} + +void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() +{ + QStringList selProtoList; + + qDebug("%s", __FUNCTION__); + + _iter->toFront(); + while(_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + qDebug("%p -- %d", p, p->protocolNumber()); + selProtoList.append(p->shortName()); + } + mpSelectedProtocolsModel->setStringList(selProtoList); +} + +void StreamConfigDialog::on_twTopLevel_currentChanged(int index) +{ + switch (index) + { + // Protocol Data + case 1: + { + // Hide the ToolBox before modifying it - else we have a crash !!! + tbProtocolData->hide(); + + // Remove all existing protocol widgets + while (tbProtocolData->count() > 0) + { + QWidget* w = tbProtocolData->widget(0); + tbProtocolData->removeItem(0); + w->setParent(0); + } + + // Repopulate the widgets - create new ones, if required + _iter->toFront(); + while (_iter->hasNext()) + { + AbstractProtocol* p = _iter->next(); + AbstractProtocolConfigForm *w = _protocolWidgets.value(p); + if (!w) { + w = OstProtocolWidgetFactory->createConfigWidget( + p->protocolNumber()); + w->loadWidget(p); + _protocolWidgets.insert(p, w); + } + tbProtocolData->addItem(w, p->name()); + } + + if (lastProtocolDataIndex < tbProtocolData->count()) + tbProtocolData->setCurrentIndex(lastProtocolDataIndex); + + tbProtocolData->show(); + break; + } + + // Stream Control + case 2: + { + StoreCurrentStream(); + break; + } + + // Packet View + case 3: + { + StoreCurrentStream(); + mpPacketModel->setSelectedProtocols(*_iter); + break; + } + + default: + break; + } + + lastProtocolDataIndex = tbProtocolData->currentIndex(); +} + +void StreamConfigDialog::update_NumPacketsAndNumBursts() +{ + if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) + leNumPackets->setEnabled(true); + else + leNumPackets->setEnabled(false); + + if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) + leNumBursts->setEnabled(true); + else + leNumBursts->setEnabled(false); +} + +#if 0 +void StreamConfigDialog::on_lePattern_editingFinished() +{ + ulong num = 0; + bool isOk; + QString str; + + num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); + lePattern->setText(uintToHexStr(num, str, 4)); + qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); +} +#endif + +/*! +Skip protocols upto and including the layer specified. +*/ +bool StreamConfigDialog::skipProtocols(int layer) +{ + _iter->toFront(); + + for (int i = ProtoMin; i <= layer; i++) + { + if(_iter->hasNext()) + { + int id; + QAbstractButton *btn; + + id = _iter->peekNext()->protocolNumber(); + btn = bgProto[i]->button(id); + if (btn) + _iter->next(); + } + } + + return true; +} + +/*! +Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled +*/ +void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) +{ + qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); + foreach(QAbstractButton *btn, protocolGroup->buttons()) + { + int id = protocolGroup->id(btn); + + if ((id != ButtonIdNone) && (id != ButtonIdOther)) + btn->setDisabled(checked); + } +} + +void StreamConfigDialog::forceProtocolNone(bool checked) +{ + QObject *btn; + + btn = sender(); + Q_ASSERT(btn != NULL); + + qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, + checked, btn, rbL1None, rbFtNone, rbL3None); + + if (btn == rbL1None) + { + if (checked) + { + bgProto[ProtoVlan]->button(ButtonIdNone)->click(); + bgProto[ProtoL2]->button(ButtonIdNone)->click(); + bgProto[ProtoPayload]->button(ButtonIdNone)->click(); + } + + disableProtocols(bgProto[ProtoVlan], checked); + disableProtocols(bgProto[ProtoL2], checked); + disableProtocols(bgProto[ProtoPayload], checked); + } + else if (btn == rbFtNone) + { + if (checked) + bgProto[ProtoL3]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL3], checked); + } + else if (btn == rbL3None) + { + if (checked) + bgProto[ProtoL4]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL4], checked); + } + else if (btn == rbL4None) + { + if (checked) + bgProto[ProtoL5]->button(ButtonIdNone)->click(); + disableProtocols(bgProto[ProtoL5], checked); + } + else + { + Q_ASSERT(1 == 0); // Unreachable code! + } +} + +void StreamConfigDialog::updateProtocol(int newId) +{ + int level; + QButtonGroup *btnGrp; + + btnGrp = static_cast(sender()); + Q_ASSERT(btnGrp != NULL); + + level = btnGrp->property("ProtocolLevel").toInt(); + Q_ASSERT(btnGrp == bgProto[level]); + + __updateProtocol(level, newId); +} + +void StreamConfigDialog::__updateProtocol(int level, int newId) +{ + int oldId; + QButtonGroup *btnGrp; + + Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); + btnGrp = bgProto[level]; + oldId = btnGrp->property("ProtocolId").toInt(); + + qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, + level, oldId, newId, isUpdateInProgress); + + if (newId == oldId) + return; + + if (!isUpdateInProgress) + { + int ret; + AbstractProtocol *p; + + ret = skipProtocols(level-1); + Q_ASSERT(ret == true); + Q_UNUSED(ret); + + Q_ASSERT(oldId != newId); + Q_ASSERT(newId != ButtonIdOther); + + switch (oldId) + { + case ButtonIdNone: + _iter->insert(OstProtocolManager->createProtocol( + newId, mpStream)); + break; + + case ButtonIdOther: + default: + Q_ASSERT(_iter->hasNext()); + p =_iter->next(); + + if (newId) + _iter->setValue(OstProtocolManager->createProtocol( + newId, mpStream)); + else + _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); + delete p; + if (level == ProtoPayload) + { + while (_iter->hasNext()) + { + p = _iter->next(); + _iter->remove(); + // Free both protocol and associated widget + delete _protocolWidgets.take(p); + delete p; + } + } + break; + } + } + + btnGrp->setProperty("ProtocolId", newId); + return; +} + +void StreamConfigDialog::updateSelectProtocolsSimpleWidget() +{ + int i; + quint32 id; + QAbstractButton *btn; + + qDebug("%s", __FUNCTION__); + + isUpdateInProgress = true; + + // Reset to default state ... + for (i = ProtoMin; i < ProtoMax; i++) + bgProto[i]->button(ButtonIdNone)->click(); + + // ... now iterate and update + _iter->toFront(); + + for (i = ProtoMin; i < ProtoMax; i++) + { + if (!_iter->hasNext()) + goto _done; + + id = _iter->next()->protocolNumber(); + btn = bgProto[i]->button(id); + + if (btn) + { + if (btn->isEnabled()) + btn->click(); + else + { + btn->setChecked(true); + __updateProtocol(i, id); + } + } + else + { + switch (i) + { + case ProtoVlan: + _iter->previous(); + break; + + case ProtoPayload: + goto _other; + + default: + btn = bgProto[ProtoPayload]->button(id); + if (btn && btn->isEnabled()) + { + btn->click(); + break; + } + else + goto _other; + } + } + } + + // If more protocol(s) beyond payload ... + if (_iter->hasNext()) + { + i = ProtoPayload; + goto _other; + } + + goto _done; + +_other: + for (int j = i; j < ProtoMax; j++) + { + // VLAN doesn't have a "Other" button + if (j == ProtoVlan) + continue; + + bgProto[j]->button(ButtonIdOther)->setChecked(true); + __updateProtocol(j, ButtonIdOther); + } + +_done: + isUpdateInProgress = false; +} + +void StreamConfigDialog::LoadCurrentStream() +{ + QString str; + + qDebug("loading mpStream %p", mpStream); + + // Meta Data + { + cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); + lePktLen->setText(str.setNum(mpStream->frameLen())); + lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); + lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); + } + + // Protocols + { + updateSelectProtocolsSimpleWidget(); + updateSelectProtocolsAdvancedWidget(); + + loadProtocolWidgets(); + } + + // Stream Control + { + switch (mpStream->sendUnit()) + { + case Stream::e_su_packets: + rbSendPackets->setChecked(true); + break; + case Stream::e_su_bursts: + rbSendBursts->setChecked(true); + break; + default: + qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); + } + + switch (mpStream->sendMode()) + { + case Stream::e_sm_fixed: + rbModeFixed->setChecked(true); + break; + case Stream::e_sm_continuous: + rbModeContinuous->setChecked(true); + break; + default: + qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); + } + + switch(mpStream->nextWhat()) + { + case Stream::e_nw_stop: + rbActionStop->setChecked(true); + break; + case Stream::e_nw_goto_next: + rbActionGotoNext->setChecked(true); + break; + case Stream::e_nw_goto_id: + rbActionGotoStream->setChecked(true); + break; + default: + qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); + } + + leNumPackets->setText(QString().setNum(mpStream->numPackets())); + leNumBursts->setText(QString().setNum(mpStream->numBursts())); + lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); + lePacketsPerSec->setText( + QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); + leBurstsPerSec->setText( + QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); + // TODO(MED): Change this when we support goto to specific stream + leStreamId->setText(QString("0")); + + leGapIsg->setText("0.0"); + } + qDebug("loading stream done"); +} + +void StreamConfigDialog::StoreCurrentStream() +{ + QString str; + bool isOk; + Stream *pStream = mpStream; + + qDebug("storing pStream %p", pStream); + + // Meta Data + pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); + pStream->setFrameLen(lePktLen->text().toULong(&isOk)); + pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); + pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); + + // Protocols + { + storeProtocolWidgets(); + } + + // Stream Control + { + if (rbSendPackets->isChecked()) + pStream->setSendUnit(Stream::e_su_packets); + if (rbSendBursts->isChecked()) + pStream->setSendUnit(Stream::e_su_bursts); + + if (rbModeFixed->isChecked()) + pStream->setSendMode(Stream::e_sm_fixed); + if (rbModeContinuous->isChecked()) + pStream->setSendMode(Stream::e_sm_continuous); + + if (rbActionStop->isChecked()) + pStream->setNextWhat(Stream::e_nw_stop); + if (rbActionGotoNext->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_next); + if (rbActionGotoStream->isChecked()) + pStream->setNextWhat(Stream::e_nw_goto_id); + + pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); + pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); + pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); + pStream->setPacketRate( + QLocale().toDouble(lePacketsPerSec->text(), &isOk)); + pStream->setBurstRate( + QLocale().toDouble(leBurstsPerSec->text(), &isOk)); + } +} + +void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) +{ + // Refresh protocol widgets in case there is any dependent data between + // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol + // selection in TextProtocol +#if 0 // FIXME: temp mask to avoid crash till we fix it + storeProtocolWidgets(); + loadProtocolWidgets(); +#endif +} + +void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) +{ + if (checked) + on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); +} + +void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) +{ + if (checked) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) +{ + if (rbSendBursts->isChecked()) + on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); +} + +void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); + + if (rbPacketsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("0.0")); + leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); + } +} + +void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk); + double bitsPerSec = burstsPerSec * + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + if (rbBurstsPerSec->isChecked()) + leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); + leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); + leGapIpg->setText(QString("0.0")); + } + qDebug("end of %s", __FUNCTION__); +} + +void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) +{ + bool isOk; + Stream *pStream = mpStream; + uint burstSize = lePacketsPerBurst->text().toULong(&isOk); + uint frameLen; + + if (pStream->lenMode() == Stream::e_fl_fixed) + frameLen = pStream->frameLen(); + else + frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; + + if (rbSendPackets->isChecked()) + { + double pktsPerSec = QLocale().toDouble(text, &isOk)/ + double((frameLen+kEthFrameOverHead)*8); + lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); + } + else if (rbSendBursts->isChecked()) + { + double burstsPerSec = QLocale().toDouble(text, &isOk)/ + double(burstSize * (frameLen + kEthFrameOverHead) * 8); + leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); + } +} + +void StreamConfigDialog::on_pbOk_clicked() +{ + QString log; + OstProto::Stream s; + + // Store dialog contents into stream + StoreCurrentStream(); + + if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) + && (mpStream->isFrameVariable())) + { + log += "In 'Interleaved Streams' transmit mode, the count for " + "varying fields at transmit time may not be same as configured\n"; + } + + mpStream->preflightCheck(log); + + if (log.length()) + { + if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::No) + return; + } + + // Copy the data from the "local working copy of stream" to "actual stream" + mpStream->protoDataCopyInto(s); + mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); + + qDebug("stream stored"); + + lastGeometry = geometry(); + lastTopLevelTabIndex = twTopLevel->currentIndex(); + lastProtocolDataIndex = tbProtocolData->currentIndex(); + + accept(); +} + diff --git a/client/streamconfigdialog.h b/client/streamconfigdialog.h new file mode 100644 index 0000000..5330414 --- /dev/null +++ b/client/streamconfigdialog.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_CONFIG_DIALOG_H +#define _STREAM_CONFIG_DIALOG_H + +#include +#include "ui_streamconfigdialog.h" +#include "port.h" +#include "stream.h" +#include "packetmodel.h" +#include "modeltest.h" + +#define MAX_MAC_ITER_COUNT 256 +#define MIN_PKT_LEN 64 +#define MAX_PKT_LEN 16384 + +/* +** TODO +** \todo Improve HexStr handling +** +*/ + +class AbstractProtocolConfigForm; + +class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog +{ + Q_OBJECT +public: + StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0); + ~StreamConfigDialog(); + +private: + + enum ButtonId + { + ButtonIdNone = 0, + ButtonIdOther = -2 + }; + + enum ProtoButtonGroup + { + ProtoMin, + ProtoL1 = 0, + ProtoVlan = 1, + ProtoL2 = 2, + ProtoL3 = 3, + ProtoL4 = 4, + ProtoL5 = 5, + ProtoPayload = 6, + ProtoMax + }; + + QButtonGroup *bgProto[ProtoMax]; + + QStringListModel *mpAvailableProtocolsModel; + QStringListModel *mpSelectedProtocolsModel; + + Port& mPort; + uint mCurrentStreamIndex; + + Stream *mpStream; + ProtocolListIterator *_iter; + QHash _protocolWidgets; + + bool isUpdateInProgress; + + PacketModel *mpPacketModel; + ModelTest *mpPacketModelTester; + + // The following static variables are used to track the "selected" tab + // for the various tab widgets so that it can be restored when the dialog + // is opened the next time. We also track the last Dialog geometry. + static QRect lastGeometry; + static int lastTopLevelTabIndex; + static int lastProtocolDataIndex; + + void setupUiExtra(); + void LoadCurrentStream(); + void StoreCurrentStream(); + void loadProtocolWidgets(); + void storeProtocolWidgets(); + +private slots: + void on_cmbPktLenMode_currentIndexChanged(QString mode); + void update_NumPacketsAndNumBursts(); + + void on_twTopLevel_currentChanged(int index); + void on_tbSelectProtocols_currentChanged(int index); + + // "Simple" Protocol Selection related + bool skipProtocols(int layer); + + void disableProtocols(QButtonGroup *protocolGroup, bool checked); + void forceProtocolNone(bool checked); + + void updateProtocol(int newId); + void __updateProtocol(int level, int newId); + + void updateSelectProtocolsSimpleWidget(); + + // "Advanced" Protocol Selection related + void when_lvAllProtocols_selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected); + void when_lvSelectedProtocols_currentChanged( + const QModelIndex ¤t, const QModelIndex &previous); + + void on_tbAdd_clicked(); + void on_tbDelete_clicked(); + void on_tbUp_clicked(); + void on_tbDown_clicked(); + + void updateSelectProtocolsAdvancedWidget(); + + // "Protocol Data" related + void on_tbProtocolData_currentChanged(int index); + + // "Stream Control" related + void on_rbPacketsPerSec_toggled(bool checked); + void on_rbBurstsPerSec_toggled(bool checked); + + void on_lePacketsPerBurst_textChanged(const QString &text); + void on_lePacketsPerSec_textChanged(const QString &text); + void on_leBurstsPerSec_textChanged(const QString &text); + void on_leBitsPerSec_textEdited(const QString &text); + + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + + void on_pbOk_clicked(); +}; + +#endif + diff --git a/client/streamconfigdialog.ui b/client/streamconfigdialog.ui new file mode 100644 index 0000000..e109c79 --- /dev/null +++ b/client/streamconfigdialog.ui @@ -0,0 +1,1481 @@ + + StreamConfigDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 634 + 507 + + + + + 0 + 0 + + + + Edit Stream + + + :/icons/stream_edit.png + + + QLineEdit:enabled[inputMask = "HH; "], +QLineEdit:enabled[inputMask = "HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH; "], +QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } + + + + true + + + + + + + + + 0 + + + + Protocol Selection + + + + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + Frame Length (including FCS) + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + Random + + + + + + + + Min + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Max + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + + + + + 0 + 0 + 592 + 269 + + + + Simple + + + + + + L1 + + + + + + None + + + true + + + + + + + Mac + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L2 + + + + + + None + + + true + + + + + + + Ethernet II + + + false + + + + + + + 802.3 Raw + + + + + + + 802.3 LLC + + + false + + + + + + + 802.3 LLC SNAP + + + + + + + false + + + Other + + + + + + + + + + true + + + L3 + + + + + + None + + + true + + + + + + + false + + + ARP + + + + + + + false + + + IPv4 + + + false + + + + + + + false + + + IPv6 + + + + + + + false + + + IP 6over4 + + + false + + + + + + + false + + + IP 4over6 + + + false + + + + + + + false + + + IP 4over4 + + + false + + + + + + + false + + + IP 6over6 + + + false + + + + + + + false + + + Other + + + + + + + + + + true + + + L5 + + + + + + None + + + true + + + + + + + false + + + Text + + + + + + + false + + + Other + + + + + + + + + + true + + + VLAN + + + false + + + false + + + + + + Untagged + + + true + + + + + + + Tagged + + + + + + + Stacked + + + + + + + + + + true + + + L4 + + + + + + None + + + true + + + + + + + false + + + ICMP + + + + + + + false + + + IGMP + + + + + + + false + + + TCP + + + + + + + false + + + UDP + + + + + + + false + + + Other + + + + + + + false + + + MLD + + + + + + + + + + true + + + Payload + + + + + + None + + + true + + + + + + + Pattern + + + false + + + + + + + Hex Dump + + + + + + + false + + + Other + + + + + + + + + + + + 0 + 0 + 250 + 135 + + + + Advanced + + + + + + + + Available Protocols + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + > + + + :/icons/arrow_right.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Selected Protocols + + + + + + + + + false + + + ^ + + + :/icons/arrow_up.png + + + + + + + false + + + v + + + :/icons/arrow_down.png + + + + + + + false + + + - + + + :/icons/delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + + + + + Protocol Data + + + + + + -1 + + + + + + + + Stream Control + + + + + + Send + + + + + + Packets + + + true + + + + + + + Bursts + + + + + + + + + + Numbers + + + + + + Number of Packets + + + leNumPackets + + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Number of Bursts + + + leNumBursts + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Packets per Burst + + + lePacketsPerBurst + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Rate + + + false + + + false + + + + + + Packets/Sec + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + Bursts/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bits/Sec + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + After this stream + + + + + + Stop + + + + + + + Goto Next Stream + + + true + + + + + + + Goto First + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + + 20 + 41 + + + + + + + + Mode + + + + + + Fixed + + + true + + + + + + + Continuous + + + + + + + + + + true + + + Gaps (in seconds) + + + false + + + false + + + + + + + + + :/icons/gaps.png + + + + + + + ISG + + + leGapIsg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IBG + + + leGapIbg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + IPG + + + leGapIpg + + + + + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 153 + 21 + + + + + + + + + Packet View + + + + + + Qt::Vertical + + + + QAbstractItemView::SelectItems + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + + + + + + + + + Prev + + + + + + + Next + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + OK + + + true + + + + + + + Cancel + + + + + + + + + + DumpView + QWidget +
    dumpview.h
    + 1 +
    +
    + + twTopLevel + cmbPktLenMode + lePktLen + lePktLenMin + lePktLenMax + rbL1None + rbL1Mac + rbL1Other + rbVlanNone + rbVlanSingle + rbVlanDouble + rbFtNone + rbFtEthernet2 + rbFt802Dot3Raw + rbFt802Dot3Llc + rbFtLlcSnap + rbFtOther + rbL3None + rbL3Arp + rbL3Ipv4 + rbL3Ipv6 + rbL3Ip6over4 + rbL3Ip4over6 + rbL3Ip4over4 + rbL3Ip6over6 + rbL3Other + rbL4None + rbL4Icmp + rbL4Igmp + rbL4Mld + rbL4Tcp + rbL4Udp + rbL4Other + rbL5None + rbL5Text + rbL5Other + rbPayloadNone + rbPayloadPattern + rbPayloadHexDump + rbPayloadOther + lvAllProtocols + tbAdd + tbUp + tbDown + tbDelete + lvSelectedProtocols + rbSendPackets + rbSendBursts + rbModeFixed + rbModeContinuous + leNumPackets + leNumBursts + lePacketsPerBurst + lePacketsPerSec + leBurstsPerSec + rbBitsPerSec + leBitsPerSec + rbActionStop + rbActionGotoNext + rbActionGotoStream + leStreamId + leGapIsg + leGapIbg + leGapIpg + tvPacketTree + pbPrev + pbNext + pbOk + pbCancel + + + + + + + pbCancel + clicked() + StreamConfigDialog + reject() + + + 623 + 496 + + + 533 + 466 + + + + + rbActionGotoStream + toggled(bool) + leStreamId + setEnabled(bool) + + + 463 + 143 + + + 463 + 177 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setEnabled(bool) + + + 30 + 68 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setEnabled(bool) + + + 30 + 95 + + + 299 + 132 + + + + + rbSendBursts + toggled(bool) + lePacketsPerBurst + setEnabled(bool) + + + 30 + 95 + + + 134 + 189 + + + + + rbPacketsPerSec + toggled(bool) + lePacketsPerSec + setEnabled(bool) + + + 299 + 82 + + + 299 + 108 + + + + + rbBurstsPerSec + toggled(bool) + leBurstsPerSec + setEnabled(bool) + + + 299 + 132 + + + 299 + 158 + + + + + rbBitsPerSec + toggled(bool) + leBitsPerSec + setEnabled(bool) + + + 299 + 182 + + + 299 + 208 + + + + + rbSendPackets + toggled(bool) + rbPacketsPerSec + setChecked(bool) + + + 95 + 70 + + + 299 + 82 + + + + + rbSendBursts + toggled(bool) + rbBurstsPerSec + setChecked(bool) + + + 96 + 98 + + + 299 + 132 + + + + + rbModeContinuous + toggled(bool) + leNumPackets + setDisabled(bool) + + + 73 + 196 + + + 164 + 108 + + + + + rbModeContinuous + toggled(bool) + leNumBursts + setDisabled(bool) + + + 96 + 199 + + + 226 + 155 + + + + +
    diff --git a/client/streamlistdelegate.cpp b/client/streamlistdelegate.cpp new file mode 100644 index 0000000..f15bc18 --- /dev/null +++ b/client/streamlistdelegate.cpp @@ -0,0 +1,194 @@ +/* +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 +*/ + +#include +#include +#include +#include + +#include "streammodel.h" +#include "streamlistdelegate.h" + +StreamListDelegate::StreamListDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + + +QWidget *StreamListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = NULL; + + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + editor = new QCheckBox(parent); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + editor = new QComboBox(parent); + static_cast(editor)->insertItems(0, + StreamModel::nextWhatOptionList()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + editor = QItemDelegate::createEditor(parent, option, index); + +_handled: + return editor; + +} + + +void StreamListDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + cb->setChecked( + index.model()->data(index, Qt::EditRole).toBool()); + goto _handled; + } + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex( + index.model()->data(index, Qt::EditRole).toInt()); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setEditorData(editor, index); + +_handled: + return; +} + + +void StreamListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + QCheckBox *cb = static_cast(editor); + model->setData(index, cb->isChecked(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamNextWhat: + { + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); + goto _handled; + } + + case StreamModel::StreamIcon: + case StreamModel::StreamName: + default: + break; + } + + QItemDelegate::setModelData(editor, model, index); + +_handled: + return; +} + + +void StreamListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + switch ((StreamModel::StreamFields) index.column()) + { + case StreamModel::StreamStatus: + { + /* + * extra 'coz QItemDelegate does it - otherwise the editor + * placement is incorrect + */ + int extra = 2 * (qApp->style()->pixelMetric( + QStyle::PM_FocusFrameHMargin, 0) + 1); + + editor->setGeometry(option.rect.translated(extra, 0)); + goto _handled; + } + case StreamModel::StreamIcon: + case StreamModel::StreamName: + case StreamModel::StreamNextWhat: + default: + break; + } + + QItemDelegate::updateEditorGeometry(editor, option, index); + +_handled: + return; +} + + +bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + /* + * Special Handling so that user can use the "Stream status" checkbox + * without double clicking first. Copied from QItemDelegate::editorEvent() + * and modified suitably + */ + if ((StreamModel::StreamFields)index.column() == + StreamModel::StreamStatus) + { + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick)) + { + QRect checkRect = check(option, option.rect, Qt::Checked); + QRect emptyRect; + doLayout(option, &checkRect, &emptyRect, &emptyRect, false); + if (!checkRect.contains(static_cast(event)->pos())) + return false; + + Qt::CheckState state = (static_cast(index.data( + Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + return model->setData(index, state, Qt::CheckStateRole); + } + } + + return QItemDelegate::editorEvent(event, model, option, index); +} + diff --git a/client/streamlistdelegate.h b/client/streamlistdelegate.h new file mode 100644 index 0000000..a98a34e --- /dev/null +++ b/client/streamlistdelegate.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#ifndef STREAM_LIST_DELEGATE_H +#define STREAM_LIST_DELEGATE_H + +#include +#include + +class StreamListDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + StreamListDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif + diff --git a/client/streammodel.cpp b/client/streammodel.cpp new file mode 100644 index 0000000..c66f02c --- /dev/null +++ b/client/streammodel.cpp @@ -0,0 +1,288 @@ +/* +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 +*/ + +#include "stream.h" +#include "streammodel.h" +#include "portgrouplist.h" +#include "qicon.h" + +StreamModel::StreamModel(PortGroupList *p, QObject *parent) + : QAbstractTableModel(parent) +{ + pgl = p; + mCurrentPort = NULL; +} + +int StreamModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (mCurrentPort) + return mCurrentPort->numStreams(); + else + return 0; +} + +int StreamModel::columnCount(const QModelIndex &/*parent*/) const +{ + int count = StreamMaxFields; + if (mCurrentPort && + (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) + count--; + + return count; +} + +Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + + switch (index.column()) + { + case StreamIcon: + break; + case StreamName: + flags |= Qt::ItemIsEditable; + break; + case StreamStatus: + flags |= Qt::ItemIsUserCheckable; + break; + case StreamNextWhat: + flags |= Qt::ItemIsEditable; + break; + default: + //qFatal("Missed case in switch!"); + break; + } + + return flags; +} + +QVariant StreamModel::data(const QModelIndex &index, int role) const +{ + // Check for a valid index + if (!index.isValid()) + return QVariant(); + + // Check for row/column limits + if (index.row() >= mCurrentPort->numStreams()) + return QVariant(); + + if (index.column() >= StreamMaxFields) + return QVariant(); + + if (mCurrentPort == NULL) + return QVariant(); + + // Return data based on field and role + switch(index.column()) + { + case StreamIcon: + { + if (role == Qt::DecorationRole) + return QIcon(":/icons/stream_edit.png"); + else + return QVariant(); + break; + } + case StreamName: + { + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + return mCurrentPort->streamByIndex(index.row())->name(); + else + return QVariant(); + break; + } + case StreamStatus: + { + if ((role == Qt::CheckStateRole)) + { + if (mCurrentPort->streamByIndex(index.row())->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + } + else + return QVariant(); + break; + } + case StreamNextWhat: + { + int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); + + if (role == Qt::DisplayRole) + return nextWhatOptionList().at(val); + else if (role == Qt::EditRole) + return val; + else + return QVariant(); + + break; + } + default: + qFatal("-------------UNHANDLED STREAM FIELD----------------"); + } + + return QVariant(); +} + +bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (mCurrentPort == NULL) + return false; + + if (index.isValid()) + { + switch (index.column()) + { + // Edit Supported Fields + case StreamName: + mCurrentPort->streamByIndex(index.row())->setName(value.toString()); + emit(dataChanged(index, index)); + return true; + + case StreamStatus: + mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); + emit(dataChanged(index, index)); + return true; + + case StreamNextWhat: + if (role == Qt::EditRole) + { + mCurrentPort->streamByIndex(index.row())->setNextWhat( + (Stream::NextWhat)value.toInt()); + emit(dataChanged(index, index)); + return true; + } + else + return false; + + // Edit Not Supported Fields + case StreamIcon: + return false; + + // Unhandled Stream Field + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + + return false; +} + +QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch(section) + { + case StreamIcon: + return QString(""); + break; + case StreamName: + return QString("Name"); + break; + case StreamStatus: + return QString(""); + break; + case StreamNextWhat: + return QString("Goto"); + break; + default: + qDebug("-------------UNHANDLED STREAM FIELD----------------"); + break; + } + } + else + return QString("%1").arg(section+1); + + return QVariant(); +} + +bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("insertRows() row = %d", row); + qDebug("insertRows() count = %d", count); + beginInsertRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + mCurrentPort->newStreamAt(row); + endInsertRows(); + + return true; +} + +bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + qDebug("removeRows() row = %d", row); + qDebug("removeRows() count = %d", count); + beginRemoveRows(QModelIndex(), row, row+count-1); + for (int i = 0; i < count; i++) + { + mCurrentPort->deleteStreamAt(row); + } + endRemoveRows(); + + return true; +} + +// --------------------- SLOTS ------------------------ + +void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) +{ + if (!current.isValid() || !pgl->isPort(current)) + { + qDebug("current is either invalid or not a port"); + mCurrentPort = NULL; + } + else + { + qDebug("change to valid port"); + // Disconnect any existing connection to avoid duplication + // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible + // with earlier Qt versions + if (mCurrentPort) + { + disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + quint16 pg = current.internalId() >> 16; + mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; + connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), + this, SLOT(when_mCurrentPort_streamListChanged(int, int))); + } + reset(); +} + +void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, + int portId) +{ + qDebug("In %s", __FUNCTION__); + if (mCurrentPort) + { + if ((quint32(portGroupId) == mCurrentPort->portGroupId()) + && (quint32(portId) == mCurrentPort->id())) + reset(); + } +} diff --git a/client/streammodel.h b/client/streammodel.h new file mode 100644 index 0000000..d559618 --- /dev/null +++ b/client/streammodel.h @@ -0,0 +1,78 @@ +/* +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 +*/ + +#ifndef _STREAM_MODEL_H +#define _STREAM_MODEL_H + +#include +#include +#include "port.h" + +class PortGroupList; + +class StreamModel : public QAbstractTableModel +{ + Q_OBJECT + + Port *mCurrentPort; + PortGroupList *pgl; + + public: + StreamModel(PortGroupList *p, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool insertRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex & parent = QModelIndex()); + +#if 0 // CleanedUp! + // FIXME(HIGH): This *is* like a kludge + QList* currentPortStreamList() + { return &mCurrentPort->mStreams; } +#endif + + public: + enum StreamFields { + StreamIcon = 0, + StreamStatus, + StreamName, + StreamNextWhat, + + StreamMaxFields + }; + + static QStringList nextWhatOptionList() + { return QStringList() << "Stop" << "Next" << "Goto first"; } + + public slots: + void setCurrentPortIndex(const QModelIndex ¤t); + + private slots: + void when_mCurrentPort_streamListChanged(int portGroupId, int portId); +}; + +#endif diff --git a/client/updater.cpp b/client/updater.cpp new file mode 100644 index 0000000..669cfb2 --- /dev/null +++ b/client/updater.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2015 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 "updater.h" + +#include +#include +#include + +extern const char* version; + +Updater::Updater() +{ + http_ = NULL; + file_ = NULL; + +#if 1 + // Tests! + Q_ASSERT(isVersionNewer("1.1", "1") == true); + Q_ASSERT(isVersionNewer("10.1", "2") == true); + Q_ASSERT(isVersionNewer("0.10", "0.2") == true); + Q_ASSERT(isVersionNewer("1.10.1", "1.2.3") == true); +#endif +} + +Updater::~Updater() +{ + delete http_; + delete file_; +} + +void Updater::checkForNewVersion() +{ + http_ = new QHttp("wiki.ostinato.googlecode.com"); + file_ = new QTemporaryFile(); + + connect(http_, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), + this, SLOT(responseReceived(QHttpResponseHeader))); + connect(http_, SIGNAL(requestFinished(int, bool)), + this, SLOT(parseXml(int, bool))); + connect(http_, SIGNAL(stateChanged(int)), + this, SLOT(stateUpdate(int))); + + file_->open(); + qDebug("Updater: PAD XML file - %s", qPrintable(file_->fileName())); + + http_->get("/hg/html/pad.xml", file_); + qDebug("Updater: %s", qPrintable(http_->currentRequest().toString() + .replace("\r\n", "\nUpdater: "))); +} + +void Updater::stateUpdate(int state) +{ + qDebug("Updater: state %d", state); +} + +void Updater::responseReceived(QHttpResponseHeader response) +{ + qDebug("Updater: HTTP/%d.%d %d %s", + response.majorVersion(), response.minorVersion(), + response.statusCode(), qPrintable(response.reasonPhrase())); +} + +void Updater::parseXml(int id, bool error) +{ + QXmlStreamReader xml; + QString newVersion; + + if (error) { + qDebug("Updater: %s", qPrintable(http_->errorString())); + goto _exit; + } + + // Close and reopen the file so that we read from the top + file_->close(); + file_->open(); + xml.setDevice(file_); + + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement() && (xml.name() == "Program_Version")) + newVersion = xml.readElementText(); + } + + qDebug("Updater: latest version = %s", qPrintable(newVersion)); + if (!newVersion.isEmpty() && isVersionNewer(newVersion, QString(version))) + emit newVersionAvailable(newVersion); + +_exit: + // Job done, time to self-destruct + deleteLater(); +} + +bool Updater::isVersionNewer(QString newVersion, QString curVersion) +{ + QStringList curVer = QString(curVersion).split('.'); + QStringList newVer = QString(newVersion).split('.'); + + for (int i = 0; i < qMin(curVer.size(), newVer.size()); i++) { + bool isOk; + if (newVer.at(i).toUInt(&isOk) > curVer.at(i).toUInt(&isOk)) + return true; + } + + if (newVer.size() > curVer.size()) + return true; + + return false; +} + + diff --git a/client/updater.h b/client/updater.h new file mode 100644 index 0000000..2d61e3d --- /dev/null +++ b/client/updater.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2015 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 +*/ + +#ifndef _UPDATER_H +#define _UPDATER_H + +#include +#include + +class QHttp; +class QTemporaryFile; + +class Updater : public QObject +{ + Q_OBJECT +public: + Updater(); + virtual ~Updater(); + void checkForNewVersion(); + static bool isVersionNewer(QString newVersion, QString curVersion); + +signals: + void newVersionAvailable(QString); + +private slots: + void stateUpdate(int state); + void responseReceived(QHttpResponseHeader response); + void parseXml(int id, bool error); + +private: + QHttp *http_; + QTemporaryFile *file_; +}; + +#endif + diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp new file mode 100644 index 0000000..15271d7 --- /dev/null +++ b/common/abstractfileformat.cpp @@ -0,0 +1,127 @@ +/* +Copyright (C) 2011 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 "abstractfileformat.h" + +#include "fileformat.h" +#include "pcapfileformat.h" +#include "pdmlfileformat.h" + +#include + +AbstractFileFormat::AbstractFileFormat() +{ + stop_ = false; +} + +AbstractFileFormat::~AbstractFileFormat() +{ +} + +QDialog* AbstractFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* AbstractFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList AbstractFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato (*)" + << "PCAP (*)" + << "PDML (*.pdml)"; +} + +void AbstractFileFormat::openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + fileName_ = fileName; + openStreams_ = &streams; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void AbstractFileFormat::saveStreamsOffline( + const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + saveStreams_ = streams; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool AbstractFileFormat::result() +{ + return result_; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (fileFormat.isMyFileFormat(fileName)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileFormat(fileName)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileFormat(fileName)) + return &pcapFileFormat; + + return NULL; +} + +AbstractFileFormat* AbstractFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (fileFormat.isMyFileType(fileType)) + return &fileFormat; + + if (pdmlFileFormat.isMyFileType(fileType)) + return &pdmlFileFormat; + + if (pcapFileFormat.isMyFileType(fileType)) + return &pcapFileFormat; + + return NULL; +} + +void AbstractFileFormat::cancel() +{ + stop_ = true; +} + +void AbstractFileFormat::run() +{ + if (op_ == kOpen) + result_ = openStreams(fileName_, *openStreams_, *error_); + else if (op_ == kSave) + result_ = saveStreams(saveStreams_, fileName_, *error_); +} diff --git a/common/abstractfileformat.h b/common/abstractfileformat.h new file mode 100644 index 0000000..1f8447d --- /dev/null +++ b/common/abstractfileformat.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ABSTRACT_FILE_FORMAT_H +#define _ABSTRACT_FILE_FORMAT_H + +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class AbstractFileFormat : public QThread +{ + Q_OBJECT +public: + AbstractFileFormat(); + virtual ~AbstractFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) = 0; + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openStreamsOffline(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + void saveStreamsOffline(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static AbstractFileFormat* fileFormatFromFile(const QString fileName); + static AbstractFileFormat* fileFormatFromType(const QString fileType); + +#if 0 + bool isMyFileFormat(const QString fileName) = 0; + bool isMyFileType(const QString fileType) = 0; +#endif + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp + { + kOpen, + kSave + }; + QString fileName_; + OstProto::StreamConfigList *openStreams_; + OstProto::StreamConfigList saveStreams_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif + diff --git a/common/abstractprotocol.cpp b/common/abstractprotocol.cpp new file mode 100644 index 0000000..230fd81 --- /dev/null +++ b/common/abstractprotocol.cpp @@ -0,0 +1,898 @@ +/* +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 +*/ + +#include "abstractprotocol.h" + +#include "protocollistiterator.h" +#include "streambase.h" + +#include + +/*! + \class AbstractProtocol + + AbstractProtocol is the base abstract class which provides the interface + for all protocols. + + All protocols supported by Ostinato are derived from AbstractProtocol. Apart + from defining the interface for a protocol, it also provides sensible default + implementations for methods so that the subclasses need not re-implement. It + also provides convenience functions for subclasses to use such as methods to + retrieve payload size, checksum etc. + + A subclass typically needs to reimplement the following methods - + - name() + - shortName() + - createInstance() + - protocolNumber() + - protoDataCopyInto() [pure virtual] + - protoDataCopyFrom() [pure virtual] + - fieldCount() + - fieldFlags() + - fieldData() + - setFieldData() + + Depending on certain conditions, subclasses may need to reimplement the + following additional methods - + - protocolIdType() + - protocolId() + - protocolFrameSize() + - isProtocolFrameValueVariable() + - isProtocolFrameSizeVariable() + - protocolFrameVariableCount() + + See the description of the methods for more information. + + Most of the above methods just need some standard boilerplate code - + the SampleProtocol implementation includes the boilerplate +*/ + +/*! + Constructs an abstract protocol for the given stream and parent + + parent is typically NULL except for protocols which are part of a + ComboProtocol +*/ +AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) +{ + //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); + mpStream = stream; + this->parent = parent; + prev = next = NULL; + _metaFieldCount = -1; + _frameFieldCount = -1; + protoSize = -1; + _hasPayload = true; +} + +/*! + Destroys the abstract protocol +*/ +AbstractProtocol::~AbstractProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ +AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, + AbstractProtocol* /* parent */) +{ + return NULL; +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) + + Subclasses MUST implement this function + + \todo convert this to a protected data member instead of a virtual function +*/ +quint32 AbstractProtocol::protocolNumber() const +{ + qFatal("Something wrong!!!"); + return 0xFFFFFFFF; +} + +/*! + \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 + + Copy the protocol's protobuf as an extension into the passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 + + Copy and update the protocol's protobuf member data variable from the + passed in protocol + + In the base class this is a pure virtual function. Subclasses MUST implement + this function. See the SampleProtocol for an example +*/ + + +/*! + Returns the full name of the protocol + + The default implementation returns a null string +*/ +QString AbstractProtocol::name() const +{ + return QString(); +} + +/*! + Returns the short name or abbreviation of the protocol + + The default implementation forms and returns an abbreviation composed + of all the upper case chars in name() \n + The default implementation caches the abbreviation on its first invocation + and subsequently returns the cached abbreviation +*/ +QString AbstractProtocol::shortName() const +{ + if (protoAbbr.isNull()) + { + QString abbr; + + for (int i = 0; i < name().size(); i++) + if (name().at(i).isUpper()) abbr.append(name().at(i)); + + if (abbr.size()) + protoAbbr = abbr; + else + protoAbbr = QString(""); + } + + return protoAbbr; +} + +/*! + Returns the number of fields in the protocol (both Frame fields and + Meta fields) + + The default implementation returns zero. Subclasses MUST implement this + function. +*/ +int AbstractProtocol::fieldCount() const +{ + return 0; +} + +/*! + Returns the number of meta fields + + The default implementation counts and returns the number of fields for which + the MetaField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count +*/ +int AbstractProtocol::metaFieldCount() const +{ + if (_metaFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(MetaField)) + c++; + _metaFieldCount = c; + } + + return _metaFieldCount; +} + +/*! + Returns the number of frame fields + + The default implementation counts and returns the number of fields for which + the FrameField flag is set\n + The default implementation caches the count on its first invocation + and subsequently returns the cached count + + Subclasses which export different sets of fields based on a opcode/type + (e.g. icmp) should re-implement this function +*/ +int AbstractProtocol::frameFieldCount() const +{ + if (_frameFieldCount < 0) + { + int c = 0; + for (int i = 0; i < fieldCount() ; i++) + if (fieldFlags(i).testFlag(FrameField)) + c++; + _frameFieldCount = c; + } + + return _frameFieldCount; +} + +/*! + Returns the field flags for the passed in field index + + The default implementation assumes all fields to be frame fields and returns + 'FrameField'. Subclasses must reimplement this method if they have any + meta fields or checksum fields. See the SampleProtocol for an example. +*/ +AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const +{ + return FrameField; +} + +/*! + Returns the requested field attribute data + + Protocols which have meta fields that vary a frame field across + streams may use the streamIndex to return the appropriate field value \n + Some field attributes e.g. FieldName may be invariant across streams\n + The FieldTextValue attribute may include additional information about + the field's value e.g. a checksum field may include "(correct)" or + "(incorrect)" alongwith the actual checksum value. \n + + The default implementation returns a empty string for FieldName and + FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for + FieldValue; subclasses are expected to return meaning values for all + these attributes. The only exception is the 'FieldBitSize' attribute - + the default implementation takes the (byte) size of FieldFrameValue, + multiplies it with 8 and returns the result - this can be used by + subclasses for fields which are an integral multiple of bytes; for + fields whose size are a non-integral multiple of bytes or smaller than + a byte, subclasses should return the correct value. Also for fields + which represent checksums, subclasses should return a value for + FieldBitSize - even if it is an integral multiple of bytes. + + \note If a subclass uses any of the below functions to derive + FieldFrameValue, the subclass should handle and return a value for + FieldBitSize to prevent endless recursion - + - protocolFrameCksum() + - protocolFramePayloadSize() +*/ +QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (attrib) + { + case FieldName: + return QString(); + case FieldBitSize: + Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), + "AbstractProtocol::fieldData()", + "FieldBitSize for checksum fields need to be handled by the subclass"); + return fieldData(index, FieldFrameValue, streamIndex). + toByteArray().size() * 8; + case FieldValue: + return 0; + case FieldFrameValue: + return QByteArray(); + case FieldTextValue: + return QString(); + + default: + qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, + attrib); + } + + return QVariant(); +} + +/*! + Sets the value of a field corresponding to index + + This method is called by the GUI code to store a user specified value into + the protocol's protoBuf. Currently this method is called with + FieldAttrib = FieldValue only. + + Returns true if field is successfully set, false otherwise. + The default implementation always returns false. Subclasses should + reimplement this method. See SampleProtocol for an example. + +*/ +bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, + FieldAttrib /*attrib*/) +{ + return false; +} + +/*! + Returns the protocolIdType for the protocol + + The default implementation returns ProtocolIdNone. If a subclass has a + protocolId field it should return the appropriate value e.g. IP protocol + will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. +*/ +AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const +{ + return ProtocolIdNone; +} + +/*! + Returns the protocol id of the protocol for the given type + + The default implementation returns 0. If a subclass represents a protocol + which has a particular protocol id, it should return the appropriate value. + If a protocol does not have an id for the given type, it should defer to + the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the + base class for the remaining ProtocolIdTypes; IP will return 0x800 for + ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. +*/ +quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const +{ + return 0; +} + +/*! + Returns the protocol id of the payload protocol (the protocol that + immediately follows the current one) + + A subclass which has a protocol id field, can use this to retrieve the + appropriate value +*/ +quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const +{ + quint32 id; + + if (next) + id = next->protocolId(type); + else if (parent) + id = parent->payloadProtocolId(type); + else + id = 0xFFFFFFFF; + + qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); + return id; +} + +/*! + Returns the protocol's size in bytes + + The default implementation sums up the individual field bit sizes and + returns it. The default implementation calculates the caches the size on + the first invocation and subsequently returns the cached size. + + If the subclass protocol has a varying protocol size, it MUST reimplement + this method, otherwise the default implementation is sufficient. +*/ +int AbstractProtocol::protocolFrameSize(int streamIndex) const +{ + if (protoSize < 0) + { + int bitsize = 0; + + for (int i = 0; i < fieldCount(); i++) + { + if (fieldFlags(i).testFlag(FrameField)) + bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); + } + protoSize = (bitsize+7)/8; + } + + qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); + return protoSize; +} + +/*! + Returns the byte offset in the packet where the protocol starts + + This method is useful only for "padding" protocols i.e. protocols which + fill up the remaining space for the user defined packet size e.g. the + PatternPayload protocol +*/ +int AbstractProtocol::protocolFrameOffset(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = prev; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->prev; + } + + if (parent) + size += parent->protocolFrameOffset(streamIndex); + + qDebug("%s: ofs = %d", __FUNCTION__, size); + return size; +} + +/*! + Returns the size of the payload in bytes. The payload includes all protocols + subsequent to the current + + This method is useful for protocols which need to fill in a payload size field +*/ +int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const +{ + int size = 0; + AbstractProtocol *p = next; + while (p) + { + size += p->protocolFrameSize(streamIndex); + p = p->next; + } + if (parent) + size += parent->protocolFramePayloadSize(streamIndex); + + qDebug("%s: payloadSize = %d", __FUNCTION__, size); + return size; +} + + +/*! + Returns a byte array encoding the protocol (and its fields) which can be + inserted into the stream's frame + + The default implementation forms and returns an ordered concatenation of + the FrameValue of all the 'frame' fields of the protocol also taking care of + fields which are not an integral number of bytes\n +*/ +QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const +{ + QByteArray proto, field; + uint bits, lastbitpos = 0; + FieldFlags flags; + + for (int i=0; i < fieldCount() ; i++) + { + flags = fieldFlags(i); + if (flags.testFlag(FrameField)) + { + bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); + if (bits == 0) + continue; + Q_ASSERT(bits > 0); + + if (forCksum && flags.testFlag(CksumField)) + { + field.resize((bits+7)/8); + field.fill('\0'); + } + else + field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); + qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, + QString(proto.toHex()).toAscii().constData()); + qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), + QString(field.toHex()).toAscii().constData()); + + if (bits == (uint) field.size() * 8) + { + if (lastbitpos == 0) + proto.append(field); + else + { + Q_ASSERT(field.size() > 0); + + char c = proto[proto.size() - 1]; + proto[proto.size() - 1] = + c | ((uchar)field.at(0) >> lastbitpos); + for (int j = 0; j < field.size() - 1; j++) + proto.append(field.at(j) << lastbitpos | + (uchar)field.at(j+1) >> lastbitpos); + proto.append(field.at(field.size() - 1) << lastbitpos); + } + } + else if (bits < (uint) field.size() * 8) + { + uchar c; + uint v; + + v = (field.size()*8) - bits; + + Q_ASSERT(v < 8); + + if (lastbitpos == 0) + { + for (int j = 0; j < field.size(); j++) + { + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar)field.at(j+1) >> (8-v)); + proto.append(c); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + else + { + Q_ASSERT(proto.size() > 0); + + for (int j = 0; j < field.size(); j++) + { + uchar d; + + c = field.at(j) << v; + if ((j+1) < field.size()) + c |= ((uchar) field.at(j+1) >> (8-v)); + d = proto[proto.size() - 1]; + proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); + if (bits > (8*j + (8 - v))) + proto.append(c << (8-lastbitpos)); + } + + lastbitpos = (lastbitpos + bits) % 8; + } + } + else // if (bits > field.size() * 8) + { + qFatal("bitsize more than FrameValue size. skipping..."); + continue; + } + } + } + + return proto; +} + +/*! + Returns true if the protocol varies one or more of its fields at run-time, + false otherwise + + The default implementation returns false. A subclass should reimplement + if it has varying fields e.g. an IP protocol that increments/decrements + the IP address with every packet +*/ +bool AbstractProtocol::isProtocolFrameValueVariable() const +{ + return (protocolFrameVariableCount() > 1); +} + +/*! + Returns true if the protocol varies its size at run-time, false otherwise + + The default implmentation returns false. A subclass should reimplement + if it varies its size at run-time e.g. a Payload protocol for a stream with + incrementing/decrementing frame lengths +*/ +bool AbstractProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + Returns the minimum number of frames required for the protocol to + vary its fields + + This is the lowest common multiple (LCM) of the counts of all the varying + fields in the protocol. Use the AbstractProtocol::lcm() static utility + function to calculate the LCM. + + The default implementation returns 1 implying that the protocol has no + varying fields. A subclass should reimplement if it has varying fields + e.g. an IP protocol that increments/decrements the IP address with + every packet +*/ +int AbstractProtocol::protocolFrameVariableCount() const +{ + return 1; +} + +/*! + Returns true if the payload content for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload content + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadValueVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadValueVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const +{ + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameSizeVariable()) + return true; + p = p->next; + } + if (parent && parent->isProtocolFramePayloadSizeVariable()) + return true; + + return false; +} + +/*! + Returns true if the payload size for a protocol varies at run-time, + false otherwise + + This is useful for subclasses which have fields dependent on payload size + (e.g. UDP has a checksum field that varies if the payload varies) +*/ +int AbstractProtocol::protocolFramePayloadVariableCount() const +{ + int count = 1; + AbstractProtocol *p = next; + + while (p) + { + if (p->isProtocolFrameValueVariable() + || p->isProtocolFrameSizeVariable()) + count = lcm(count, p->protocolFrameVariableCount()); + p = p->next; + } + if (parent && (parent->isProtocolFramePayloadValueVariable() + || parent->isProtocolFramePayloadSizeVariable())) + count = lcm(count, parent->protocolFramePayloadVariableCount()); + + return false; +} + +/*! + Returns true if the protocol typically contains a payload or other protocols + following it e.g. TCP, UDP have payloads, while ARP, IGMP do not + + The default implementation returns true. If a subclass does not have a + payload, it should set the _hasPayload data member to false +*/ +bool AbstractProtocol::protocolHasPayload() const +{ + return _hasPayload; +} + +/*! + Returns the checksum (of the requested type) of the protocol's contents + + Useful for protocols which have a checksum field + + \note If a subclass uses protocolFrameCksum() from within fieldData() to + derive a cksum field, it MUST handle and return the 'FieldBitSize' + attribute also for that particular field instead of using the default + AbstractProtocol implementation for 'FieldBitSize' - this is required + to prevent infinite recursion +*/ +quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + static int recursionCount = 0; + quint32 cksum = 0xFFFFFFFF; + + recursionCount++; + Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); + + switch(cksumType) + { + case CksumIp: + { + QByteArray fv; + quint16 *ip; + quint32 len, sum = 0; + + fv = protocolFrameValue(streamIndex, true); + ip = (quint16*) fv.constData(); + len = fv.size(); + + while(len > 1) + { + sum += *ip; + if(sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + ip++; + len -= 2; + } + + if (len) + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = qFromBigEndian((quint16) ~sum); + break; + } + + case CksumTcpUdp: + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); + sum += (quint16) ~cks; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + break; + } + default: + break; + } + + recursionCount--; + return cksum; +} + +/*! + Returns the checksum of the requested type for the protocol's header + + This is useful for subclasses which needs the header's checksum e.g. TCP/UDP + require a "Pseudo-IP" checksum. The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIpPseudo + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = prev; + + Q_ASSERT(cksumType == CksumIpPseudo); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->prev; + } + if (parent) + { + cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +/*! + Returns the checksum of the requested type for the protocol's payload + + This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP + require a IP checksum of the payload (to be combined with other checksums to + derive the final checksum). The checksum is limited to the specified + scope. + + Currently the default implementation supports only type CksumIp + + \note The default value for cksumScope is different for + protocolFrameHeaderCksum() and protocolFramePayloadCksum() +*/ +quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, + CksumType cksumType, CksumScope cksumScope) const +{ + quint32 sum = 0; + quint16 cksum; + AbstractProtocol *p = next; + + Q_ASSERT(cksumType == CksumIp); + + while (p) + { + cksum = p->protocolFrameCksum(streamIndex, cksumType); + sum += (quint16) ~cksum; + if (cksumScope == CksumScopeAdjacentProtocol) + goto out; + p = p->next; + } + + if (parent) + { + cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, + cksumScope); + sum += (quint16) ~cksum; + } + +out: + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (quint16) ~sum; +} + +// Stein's binary GCD algo - from wikipedia +quint64 AbstractProtocol::gcd(quint64 u, quint64 v) +{ + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Let shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + quint64 diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +quint64 AbstractProtocol::lcm(quint64 u, quint64 v) +{ +#if 0 + /* LCM(0,x) := x */ + if (u == 0 || v == 0) + return u | v; +#else + /* For our use case, neither u nor v can ever be 0, the minimum + value is 1; we do this correction silently here */ + if (u == 0) u = 1; + if (v == 0) v = 1; + + if (u == 1 || v == 1) + return (u * v); +#endif + + return (u * v)/gcd(u, v); +} + diff --git a/common/abstractprotocol.h b/common/abstractprotocol.h new file mode 100644 index 0000000..84388ae --- /dev/null +++ b/common/abstractprotocol.h @@ -0,0 +1,160 @@ +/* +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 +*/ + +#ifndef _ABSTRACT_PROTOCOL_H +#define _ABSTRACT_PROTOCOL_H + +#include +#include +#include +#include +#include +#include + +//#include "../rpc/pbhelper.h" +#include "protocol.pb.h" + +#define BASE_BIN (2) +#define BASE_OCT (8) +#define BASE_DEC (10) +#define BASE_HEX (16) + +class StreamBase; +class ProtocolListIterator; + +class AbstractProtocol +{ + template + friend class ComboProtocol; + friend class ProtocolListIterator; + +private: + mutable int _metaFieldCount; + mutable int _frameFieldCount; + mutable int protoSize; + mutable QString protoAbbr; + +protected: + StreamBase *mpStream; //!< Stream that this protocol belongs to + AbstractProtocol *parent; //!< Parent protocol, if any + AbstractProtocol *prev; //!< Protocol preceding this protocol + AbstractProtocol *next; //!< Protocol succeeding this protocol + + //! Is protocol typically followed by payload or another protocol + bool _hasPayload; + +public: + //! Properties of a field, can be OR'd + enum FieldFlag { + FrameField = 0x1, //!< field appears in frame content + MetaField = 0x2, //!< field does not appear in frame, is meta data + CksumField = 0x4 //!< field is a checksum and appears in frame content + }; + Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd + + //! Various attributes of a field + enum FieldAttrib { + FieldName, //!< name + FieldValue, //!< value in host byte order (user editable) + FieldTextValue, //!< value as text + FieldFrameValue, //!< frame encoded value in network byte order + FieldBitSize, //!< size in bits + }; + + //! Supported Protocol Id types + enum ProtocolIdType { + ProtocolIdNone, //!< Marker representing non-existent protocol id + ProtocolIdLlc, //!< LLC (802.2) + ProtocolIdEth, //!< Ethernet II + ProtocolIdIp, //!< IP + ProtocolIdTcpUdp, //!< TCP/UDP Port Number + }; + + //! Supported checksum types + enum CksumType { + CksumIp, //!< Standard IP Checksum + CksumIpPseudo, //!< Standard checksum for Pseudo-IP header + CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP + + CksumMax //!< Marker for number of cksum types + }; + + //! Supported checksum scopes + enum CksumScope { + CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol + CksumScopeAllProtocols, //!< Cksum over all the protocols + }; + + AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~AbstractProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + int metaFieldCount() const; + virtual int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize(int streamIndex = 0) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + bool protocolHasPayload() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAdjacentProtocol) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp, + CksumScope cksumScope = CksumScopeAllProtocols) const; + + static quint64 lcm(quint64 u, quint64 v); + static quint64 gcd(quint64 u, quint64 v); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); + +#endif diff --git a/common/abstractprotocolconfig.h b/common/abstractprotocolconfig.h new file mode 100644 index 0000000..f9fd971 --- /dev/null +++ b/common/abstractprotocolconfig.h @@ -0,0 +1,120 @@ +/* +Copyright (C) 2013-2014 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 +*/ +#ifndef _ABSTRACT_PROTOCOL_CONFIG_H +#define _ABSTRACT_PROTOCOL_CONFIG_H + +#include + +class AbstractProtocol; + +/*! + Convenience Macro - can be used by loadWidget() methods +*/ +#define uintToHexStr(num, bytes) \ + QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) + +class AbstractProtocolConfigForm : public QWidget +{ + Q_OBJECT +public: +/*! + Constructs the widget +*/ + AbstractProtocolConfigForm(QWidget *parent = 0) + : QWidget(parent) + { + // Do nothing! + } + +/*! + Destroys the widget +*/ + virtual ~AbstractProtocolConfigForm() + { + // Do nothing! + } + +/*! + Allocates and returns a new instance of the widget. + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function +*/ + static AbstractProtocolConfigForm* createInstance() + { + return NULL; + } + +/*! + Loads data from the protocol using it's fieldData() method into this + widget. Any conversion to user friendly display/editing formats (e.g. + hex format) SHOULD be done by this method. + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void loadWidget(AbstractProtocol* /*proto*/) + { + // Do nothing! + } + +/*! + Stores data from this widget into the protocol using the protocol's + setFieldData() method. Field values MUST be converted from any + user friendly display/editing formats (e.g. hex format) to simple + Qt-style integers/strings before passing to setFieldData() + + Subclasses MUST implement this function. See the SampleProtocol for + an example +*/ + virtual void storeWidget(AbstractProtocol* /*proto*/) + { + // Do nothing! + } + +/*! + Convenience Method - can be used by storeWidget() implementations +*/ + uint hexStrToUInt(QString text, bool *ok=NULL) + { + bool isOk; + uint a_uint = text.remove(QChar(' ')).toUInt(&isOk, 16); + + if (ok) + *ok = isOk; + + return a_uint; + } + +/*! + Convenience Method - can be used by storeWidget() implementations +*/ + quint64 hexStrToUInt64(QString text, bool *ok=NULL) + { + bool isOk; + quint64 a_uint = text.remove(QChar(' ')).toULongLong(&isOk, 16); + + if (ok) + *ok = isOk; + + return a_uint; + } +}; + +#endif diff --git a/common/arp.cpp b/common/arp.cpp new file mode 100644 index 0000000..aee20ce --- /dev/null +++ b/common/arp.cpp @@ -0,0 +1,825 @@ +/* +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 +*/ + +#include "arp.h" + +#include +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() + +ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ + _hasPayload = false; +} + +ArpProtocol::~ArpProtocol() +{ +} + +AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new ArpProtocol(stream, parent); +} + +quint32 ArpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kArpFieldNumber; +} + +void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::arp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::arp)) + data.MergeFrom(protocol.GetExtension(OstProto::arp)); +} + +QString ArpProtocol::name() const +{ + return QString("Address Resolution Protocol"); +} + +QString ArpProtocol::shortName() const +{ + return QString("ARP"); +} + +/*! + Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +#if 0 +AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} +#endif + +/*! + Return the protocolId for your protocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 ArpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdEth: return 0x0806; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int ArpProtocol::fieldCount() const +{ + return arp_fieldCount; +} + +AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case arp_hwType: + case arp_protoType: + + case arp_hwAddrLen: + case arp_protoAddrLen: + + case arp_opCode: + + case arp_senderHwAddr: + case arp_senderProtoAddr: + case arp_targetHwAddr: + case arp_targetProtoAddr: + break; + + case arp_senderHwAddrMode: + case arp_senderHwAddrCount: + + case arp_senderProtoAddrMode: + case arp_senderProtoAddrCount: + case arp_senderProtoAddrMask: + + case arp_targetHwAddrMode: + case arp_targetHwAddrCount: + + case arp_targetProtoAddrMode: + case arp_targetProtoAddrCount: + case arp_targetProtoAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case arp_hwType: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Type"); + case FieldValue: + return data.hw_type(); + case FieldTextValue: + return QString("%1").arg(data.hw_type()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_protoType: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Type"); + case FieldValue: + return data.proto_type(); + case FieldTextValue: + return QString("%1").arg(data.proto_type(), 4, BASE_HEX, + QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_hwAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Hardware Address Length"); + case FieldValue: + return data.hw_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.hw_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.hw_addr_len()); + default: + break; + } + break; + } + + case arp_protoAddrLen: + { + switch(attrib) + { + case FieldName: + return QString("Protocol Address Length"); + case FieldValue: + return data.proto_addr_len(); + case FieldTextValue: + return QString("%1").arg(data.proto_addr_len()); + case FieldFrameValue: + return QByteArray(1, (char) data.proto_addr_len()); + default: + break; + } + break; + } + + case arp_opCode: + { + switch(attrib) + { + case FieldName: + return QString("Operation Code"); + case FieldValue: + return data.op_code(); + case FieldTextValue: + return QString("%1").arg(data.op_code()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case arp_senderHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.sender_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.sender_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.sender_hw_addr_count()) * + hwAddrStep; + hwAddr = data.sender_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.sender_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToMacStr(hwAddr); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_senderProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.sender_proto_addr_mode()) + { + case OstProto::Arp::kFixedHost: + protoAddr = data.sender_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) + u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.sender_proto_addr_count(); + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (((data.sender_proto_addr() + & ~data.sender_proto_addr_mask()) - u) + & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.sender_proto_addr() + & data.sender_proto_addr_mask(); + host = (qrand() & ~data.sender_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled sender_proto_addr_mode = %d", + data.sender_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + case arp_targetHwAddr: + { + int u; + const int hwAddrStep = 1; + quint64 hwAddr = 0; + + switch (data.target_hw_addr_mode()) + { + case OstProto::Arp::kFixed: + hwAddr = data.target_hw_addr(); + break; + case OstProto::Arp::kIncrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() + u; + break; + case OstProto::Arp::kDecrement: + u = (streamIndex % data.target_hw_addr_count()) * + hwAddrStep; + hwAddr = data.target_hw_addr() - u; + break; + default: + qWarning("Unhandled hw_addr_mode %d", + data.target_hw_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address"); + case FieldValue: + return hwAddr; + case FieldTextValue: + return uintToMacStr(hwAddr); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + case arp_targetProtoAddr: + { + int u; + quint32 subnet, host, protoAddr = 0; + + switch(data.target_proto_addr_mode()) + { + case OstProto::Arp::kFixed: + protoAddr = data.target_proto_addr(); + break; + case OstProto::Arp::kIncrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) + u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kDecrementHost: + u = streamIndex % data.target_proto_addr_count(); + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (((data.target_proto_addr() + & ~data.target_proto_addr_mask()) - u) + & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + case OstProto::Arp::kRandomHost: + subnet = data.target_proto_addr() + & data.target_proto_addr_mask(); + host = (qrand() & ~data.target_proto_addr_mask()); + protoAddr = subnet | host; + break; + default: + qWarning("Unhandled target_proto_addr_mode = %d", + data.target_proto_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address"); + case FieldValue: + return protoAddr; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(protoAddr).toString(); + default: + break; + } + break; + } + + // Meta fields + case arp_senderHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Mode"); + case FieldValue: + return data.sender_hw_addr_mode(); + default: + break; + } + break; + case arp_senderHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Hardware Address Count"); + case FieldValue: + return data.sender_hw_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mode"); + case FieldValue: + return data.sender_proto_addr_mode(); + default: + break; + } + break; + case arp_senderProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Count"); + case FieldValue: + return data.sender_proto_addr_count(); + default: + break; + } + break; + case arp_senderProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Sender Protocol Address Mask"); + case FieldValue: + return data.sender_proto_addr_mask(); + default: + break; + } + break; + + case arp_targetHwAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Mode"); + case FieldValue: + return data.target_hw_addr_mode(); + default: + break; + } + break; + case arp_targetHwAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Hardware Address Count"); + case FieldValue: + return data.target_hw_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMode: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mode"); + case FieldValue: + return data.target_proto_addr_mode(); + default: + break; + } + break; + case arp_targetProtoAddrCount: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Count"); + case FieldValue: + return data.target_proto_addr_count(); + default: + break; + } + break; + case arp_targetProtoAddrMask: + switch(attrib) + { + case FieldName: + return QString("Target Protocol Address Mask"); + case FieldValue: + return data.target_proto_addr_mask(); + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool ArpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case arp_hwType: + { + uint hwType = value.toUInt(&isOk); + if (isOk) + data.set_hw_type(hwType); + break; + } + case arp_protoType: + { + uint protoType = value.toUInt(&isOk); + if (isOk) + data.set_proto_type(protoType); + break; + } + case arp_hwAddrLen: + { + uint hwAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_hw_addr_len(hwAddrLen); + break; + } + case arp_protoAddrLen: + { + uint protoAddrLen = value.toUInt(&isOk); + if (isOk) + data.set_proto_addr_len(protoAddrLen); + break; + } + case arp_opCode: + { + uint opCode = value.toUInt(&isOk); + if (isOk) + data.set_op_code(opCode); + break; + } + + case arp_senderHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_sender_hw_addr(hwAddr); + break; + } + case arp_senderHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); + else + isOk = false; + break; + } + case arp_senderHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_hw_addr_count(count); + break; + } + + case arp_senderProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr(protoAddr); + break; + } + case arp_senderProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_sender_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_senderProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_count(count); + break; + } + case arp_senderProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_sender_proto_addr_mask(mask); + break; + } + + case arp_targetHwAddr: + { + quint64 hwAddr = value.toULongLong(&isOk); + if (isOk) + data.set_target_hw_addr(hwAddr); + break; + } + case arp_targetHwAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.HwAddrMode_IsValid(mode)) + data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetHwAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_hw_addr_count(count); + break; + } + + case arp_targetProtoAddr: + { + uint protoAddr = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr(protoAddr); + break; + } + case arp_targetProtoAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.ProtoAddrMode_IsValid(mode)) + data.set_target_proto_addr_mode( + (OstProto::Arp::ProtoAddrMode)mode); + else + isOk = false; + break; + } + case arp_targetProtoAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_count(count); + break; + } + case arp_targetProtoAddrMask: + { + uint mask = value.toUInt(&isOk); + if (isOk) + data.set_target_proto_addr_mask(mask); + break; + } + + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool ArpProtocol::isProtocolFrameValueVariable() const +{ + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed) + || fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + return true; + + return false; +} + +int ArpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); + } + + if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() + != uint(OstProto::Arp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); + } + + return count; +} diff --git a/common/arp.h b/common/arp.h new file mode 100644 index 0000000..6b674f9 --- /dev/null +++ b/common/arp.h @@ -0,0 +1,103 @@ +/* +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 +*/ + +#ifndef _ARP_H +#define _ARP_H + +#include "abstractprotocol.h" +#include "arp.pb.h" + +/* +Arp Protocol Frame Format - + +------+------+------+------+------+---------+-------+---------+-------+ + | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | + | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | + +------+------+------+------+------+---------+-------+---------+-------+ +Figures in brackets represent field width in bytes +*/ + +class ArpProtocol : public AbstractProtocol +{ +public: + enum arpfield + { + // Frame Fields + arp_hwType, + arp_protoType, + + arp_hwAddrLen, + arp_protoAddrLen, + + arp_opCode, + + arp_senderHwAddr, + arp_senderProtoAddr, + arp_targetHwAddr, + arp_targetProtoAddr, + + // Meta Fields + arp_senderHwAddrMode, + arp_senderHwAddrCount, + + arp_senderProtoAddrMode, + arp_senderProtoAddrCount, + arp_senderProtoAddrMask, + + arp_targetHwAddrMode, + arp_targetHwAddrCount, + + arp_targetProtoAddrMode, + arp_targetProtoAddrCount, + arp_targetProtoAddrMask, + + + arp_fieldCount + }; + + ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~ArpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Arp data; +}; + +#endif diff --git a/common/arp.proto b/common/arp.proto new file mode 100644 index 0000000..12ccebb --- /dev/null +++ b/common/arp.proto @@ -0,0 +1,67 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// ARP Protocol +message Arp { + + enum HwAddrMode { + kFixed = 0; + kIncrement = 1; + kDecrement = 2; + } + + enum ProtoAddrMode { + kFixedHost = 0; + kIncrementHost = 1; + kDecrementHost = 2; + kRandomHost = 3; + } + + optional uint32 hw_type = 1 [default = 1]; + optional uint32 proto_type = 2 [default = 0x800]; + optional uint32 hw_addr_len = 3 [default = 6]; + optional uint32 proto_addr_len = 4 [default = 4]; + optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request + + optional uint64 sender_hw_addr = 6; + optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; + optional uint32 sender_hw_addr_count = 8 [default = 16]; + + optional uint32 sender_proto_addr = 9; + optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; + optional uint32 sender_proto_addr_count = 11 [default = 16]; + optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; + + optional uint64 target_hw_addr = 13; + optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; + optional uint32 target_hw_addr_count = 15 [default = 16]; + + optional uint32 target_proto_addr = 16; + optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; + optional uint32 target_proto_addr_count = 18 [default = 16]; + optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; +} + +extend Protocol { + optional Arp arp = 300; +} diff --git a/common/arp.ui b/common/arp.ui new file mode 100644 index 0000000..6f4c847 --- /dev/null +++ b/common/arp.ui @@ -0,0 +1,518 @@ + + Arp + + + + 0 + 0 + 528 + 286 + + + + Form + + + + + + + + + + + + Hardware Type + + + hwType + + + + + + + false + + + + + + + Hardware Address Length + + + hwAddrLen + + + + + + + false + + + + + + + Protocol Type + + + protoType + + + + + + + false + + + + + + + Protocol Address Length + + + protoAddrLen + + + + + + + false + + + + + + + + + + + + + + + + Operation Code + + + + + + + + 1 + 0 + + + + true + + + QComboBox::NoInsert + + + + + + + Qt::Horizontal + + + + 161 + 20 + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Sender Hardware + + + senderHwAddr + + + + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + Sender Protocol + + + senderProtoAddr + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Target Hardware + + + targetHwAddr + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + 255 + 0 + + + + + + + 0 + + + + + + + Target Protocol + + + targetProtoAddr + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 255 + 0 + + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + hwType + protoType + hwAddrLen + protoAddrLen + senderHwAddr + senderHwAddrMode + senderHwAddrCount + senderProtoAddr + senderProtoAddrMode + senderProtoAddrCount + senderProtoAddrMask + targetHwAddr + targetHwAddrMode + targetHwAddrCount + targetProtoAddr + targetProtoAddrMode + targetProtoAddrCount + targetProtoAddrMask + + + +
    diff --git a/common/arpconfig.cpp b/common/arpconfig.cpp new file mode 100644 index 0000000..e24dfe4 --- /dev/null +++ b/common/arpconfig.cpp @@ -0,0 +1,270 @@ +/* +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 +*/ + +#include "arpconfig.h" +#include "arp.h" + +#include + +ArpConfigForm::ArpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + opCodeCombo->addItem(1, "ARP Request"); + opCodeCombo->addItem(2, "ARP Reply"); + + connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderHwAddrMode_currentIndexChanged(int))); + connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); + connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetHwAddrMode_currentIndexChanged(int))); + connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), + SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); +} + +ArpConfigForm::~ArpConfigForm() +{ +} + +ArpConfigForm* ArpConfigForm::createInstance() +{ + return new ArpConfigForm; +} + +void ArpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hwType->setText( + proto->fieldData( + ArpProtocol::arp_hwType, + AbstractProtocol::FieldValue + ).toString()); + protoType->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_protoType, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + hwAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_hwAddrLen, + AbstractProtocol::FieldValue + ).toString()); + protoAddrLen->setText( + proto->fieldData( + ArpProtocol::arp_protoAddrLen, + AbstractProtocol::FieldValue + ).toString()); + + opCodeCombo->setValue( + proto->fieldData( + ArpProtocol::arp_opCode, + AbstractProtocol::FieldValue + ).toUInt()); + + senderHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_senderHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + senderHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + senderProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + senderProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + senderProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + senderProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_senderProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + targetHwAddr->setText(uintToHexStr( + proto->fieldData( + ArpProtocol::arp_targetHwAddr, + AbstractProtocol::FieldValue + ).toULongLong(), 6)); + targetHwAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetHwAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetHwAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetHwAddrCount, + AbstractProtocol::FieldValue + ).toString()); + + targetProtoAddr->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + targetProtoAddrMode->setCurrentIndex( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + targetProtoAddrCount->setText( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrCount, + AbstractProtocol::FieldValue + ).toString()); + targetProtoAddrMask->setText(QHostAddress( + proto->fieldData( + ArpProtocol::arp_targetProtoAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void ArpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + ArpProtocol::arp_hwType, + hwType->text()); + proto->setFieldData( + ArpProtocol::arp_protoType, + hexStrToUInt(protoType->text())); + proto->setFieldData( + ArpProtocol::arp_hwAddrLen, + hwAddrLen->text()); + proto->setFieldData( + ArpProtocol::arp_protoAddrLen, + protoAddrLen->text()); + + proto->setFieldData( + ArpProtocol::arp_opCode, + opCodeCombo->currentValue()); + + proto->setFieldData( + ArpProtocol::arp_senderHwAddr, + hexStrToUInt64(senderHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrMode, + senderHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderHwAddrCount, + senderHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_senderProtoAddr, + QHostAddress(senderProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMode, + senderProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrCount, + senderProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_senderProtoAddrMask, + QHostAddress(senderProtoAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + ArpProtocol::arp_targetHwAddr, + hexStrToUInt64(targetHwAddr->text())); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrMode, + targetHwAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetHwAddrCount, + targetHwAddrCount->text()); + + proto->setFieldData( + ArpProtocol::arp_targetProtoAddr, + QHostAddress(targetProtoAddr->text()).toIPv4Address()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMode, + targetProtoAddrMode->currentIndex()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrCount, + targetProtoAddrCount->text()); + proto->setFieldData( + ArpProtocol::arp_targetProtoAddrMask, + QHostAddress(targetProtoAddrMask->text()).toIPv4Address()); +} + +/* + * ------------ Private Slots -------------- + */ + +void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + senderHwAddrCount->setDisabled(true); + else + senderHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixed) + targetHwAddrCount->setDisabled(true); + else + targetHwAddrCount->setEnabled(true); +} + +void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + senderProtoAddrCount->setDisabled(true); + senderProtoAddrMask->setDisabled(true); + } + else + { + senderProtoAddrCount->setEnabled(true); + senderProtoAddrMask->setEnabled(true); + } +} + +void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Arp::kFixedHost) + { + targetProtoAddrCount->setDisabled(true); + targetProtoAddrMask->setDisabled(true); + } + else + { + targetProtoAddrCount->setEnabled(true); + targetProtoAddrMask->setEnabled(true); + } +} + diff --git a/common/arpconfig.h b/common/arpconfig.h new file mode 100644 index 0000000..15aa4bc --- /dev/null +++ b/common/arpconfig.h @@ -0,0 +1,46 @@ +/* +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 +*/ + +#ifndef _ARP_CONFIG_H +#define _ARP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_arp.h" + +class ArpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Arp +{ + Q_OBJECT +public: + ArpConfigForm(QWidget *parent = 0); + virtual ~ArpConfigForm(); + + static ArpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +private slots: + void on_senderHwAddrMode_currentIndexChanged(int index); + void on_senderProtoAddrMode_currentIndexChanged(int index); + void on_targetHwAddrMode_currentIndexChanged(int index); + void on_targetProtoAddrMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/arppdml.cpp b/common/arppdml.cpp new file mode 100644 index 0000000..9580db7 --- /dev/null +++ b/common/arppdml.cpp @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 "arppdml.h" + +#include "arp.pb.h" + +PdmlArpProtocol::PdmlArpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kArpFieldNumber; + + fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); + fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); + fieldMap_.insert("arp.src.proto_ipv4", + OstProto::Arp::kSenderProtoAddrFieldNumber); + fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); + fieldMap_.insert("arp.dst.proto_ipv4", + OstProto::Arp::kTargetProtoAddrFieldNumber); +} + +PdmlProtocol* PdmlArpProtocol::createInstance() +{ + return new PdmlArpProtocol(); +} + diff --git a/common/arppdml.h b/common/arppdml.h new file mode 100644 index 0000000..039ed9b --- /dev/null +++ b/common/arppdml.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ARP_PDML_H +#define _ARP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlArpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlArpProtocol(); +}; + +#endif diff --git a/common/comboprotocol.h b/common/comboprotocol.h new file mode 100644 index 0000000..97fe960 --- /dev/null +++ b/common/comboprotocol.h @@ -0,0 +1,190 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_H +#define _COMBO_PROTOCOL_H + +#include "abstractprotocol.h" + +template +class ComboProtocol : public AbstractProtocol +{ +protected: + ProtoA *protoA; + ProtoB *protoB; + +public: + ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) + : AbstractProtocol(stream, parent) + { + protoA = new ProtoA(stream, this); + protoB = new ProtoB(stream, this); + protoA->next = protoB; + protoB->prev = protoA; + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, protoA, protoB); + } + + virtual ~ComboProtocol() + { + delete protoA; + delete protoB; + } + + static ComboProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new ComboProtocol(stream, parent); + } + + virtual quint32 protocolNumber() const + { + return protoNumber; + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + protoA->protoDataCopyInto(protocol); + protoB->protoDataCopyInto(protocol); + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber()) + { + OstProto::Protocol proto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() - but since the + // input param 'protocol' is 'const', we work on a copy + proto.CopyFrom(protocol); + + proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + protoA->protoDataCopyFrom(proto); + + proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + protoB->protoDataCopyFrom(proto); + } + } + + virtual QString name() const + { + return protoA->name() + "/" + protoB->name(); + } + virtual QString shortName() const + { + return protoA->shortName() + "/" + protoB->shortName(); + } + + virtual ProtocolIdType protocolIdType() const + { + return protoB->protocolIdType(); + } + + virtual quint32 protocolId(ProtocolIdType type) const + { + return protoA->protocolId(type); + } + //quint32 payloadProtocolId(ProtocolIdType type) const; + + virtual int fieldCount() const + { + return protoA->fieldCount() + protoB->fieldCount(); + } + //virtual int metaFieldCount() const; + //int frameFieldCount() const; + + virtual FieldFlags fieldFlags(int index) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldFlags(index); + else + return protoB->fieldFlags(index - cnt); + } + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->fieldData(index, attrib, streamIndex); + else + return protoB->fieldData(index - cnt, attrib, streamIndex); + } + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue) + { + int cnt = protoA->fieldCount(); + + if (index < cnt) + return protoA->setFieldData(index, value, attrib); + else + return protoB->setFieldData(index - cnt, value, attrib); + } + +#if 0 + QByteArray protocolFrameValue(int streamIndex = 0, + bool forCksum = false) const; + virtual int protocolFrameSize() const; + int protocolFrameOffset() const; + int protocolFramePayloadSize() const; +#endif + + virtual bool isProtocolFrameValueVariable() const + { + return (protoA->isProtocolFrameValueVariable() + || protoB->isProtocolFrameValueVariable()); + } + + virtual bool isProtocolFrameSizeVariable() const + { + return (protoA->isProtocolFrameSizeVariable() + || protoB->isProtocolFrameSizeVariable()); + } + virtual int protocolFrameVariableCount() const + { + return AbstractProtocol::lcm( + protoA->protocolFrameVariableCount(), + protoB->protocolFrameVariableCount()); + } + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const + { + // For a Pseudo IP cksum, we assume it is the succeeding protocol + // that is requesting it and hence return protoB's cksum; + if (cksumType == CksumIpPseudo) + return protoB->protocolFrameCksum(streamIndex, cksumType); + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); + } +#if 0 + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +#endif + template friend class ComboProtocolConfigForm; +}; + +#endif diff --git a/common/comboprotocolconfig.h b/common/comboprotocolconfig.h new file mode 100644 index 0000000..d77627f --- /dev/null +++ b/common/comboprotocolconfig.h @@ -0,0 +1,100 @@ +/* +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 +*/ + +#ifndef _COMBO_PROTOCOL_CONFIG_H +#define _COMBO_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "comboprotocol.h" + +template +class ComboProtocolConfigForm : + public AbstractProtocolConfigForm +{ +public: + ComboProtocolConfigForm(QWidget *parent = 0) + : AbstractProtocolConfigForm(parent) + { + QVBoxLayout *layout = new QVBoxLayout; + + formA = new FormA(this); + formB = new FormB(this); + + layout->addWidget(formA); + layout->addWidget(formB); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, + protoNumber, formA, formB); + } + + virtual ~ComboProtocolConfigForm() + { + formA->setParent(0); + formB->setParent(0); + + delete formA; + delete formB; + } + + static ComboProtocolConfigForm* createInstance() + { + return new ComboProtocolConfigForm; + } + + virtual void loadWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->loadWidget(comboProto->protoA); + formB->loadWidget(comboProto->protoB); + } + virtual void storeWidget(AbstractProtocol *proto) + { + class ComboProtocol *comboProto = + dynamic_cast*>(proto); + + Q_ASSERT_X(comboProto != NULL, + QString("ComboProtocolConfigForm{%1}::loadWidget()") + .arg(protoNumber).toAscii().constData(), + QString("Protocol{%1} is not a instance of ComboProtocol") + .arg(proto->protocolNumber()).toAscii().constData()); + + formA->storeWidget(comboProto->protoA); + formB->storeWidget(comboProto->protoB); + } + +protected: + FormA *formA; + FormB *formB; +}; + +#endif diff --git a/common/crc32c.cpp b/common/crc32c.cpp new file mode 100644 index 0000000..b4206f8 --- /dev/null +++ b/common/crc32c.cpp @@ -0,0 +1,134 @@ +/* +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 +*/ + +/******************************************************************* +** IMPORTANT NOTE: +** This code is from RFC 4960 Stream Control Transmission Protocol +** It has been modified suitably while keeping the algorithm intact. +********************************************************************/ + +#include "crc32c.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +quint32 crc_c[256] = +{ + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, +}; + +quint32 checksumCrc32C(quint8 *buffer, uint length) +{ + uint i; + quint32 crc32 = ~0L; + quint32 result; + quint8 byte0,byte1,byte2,byte3; + + for (i = 0; i < length; i++) { + CRC32C(crc32, buffer[i]); + } + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polynomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return ( crc32 ); +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..84cdc76 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,23 @@ +/* +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 +*/ + +#include + +quint32 checksumCrc32C(quint8 *buffer, uint length); + diff --git a/common/dot2llc.h b/common/dot2llc.h new file mode 100644 index 0000000..b858914 --- /dev/null +++ b/common/dot2llc.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_LLC_H +#define _DOT2_LLC_H + +#include "comboprotocol.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocol Dot2LlcProtocol; + +#endif diff --git a/common/dot2llc.proto b/common/dot2llc.proto new file mode 100644 index 0000000..8223650 --- /dev/null +++ b/common/dot2llc.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; +import "dot3.proto"; +import "llc.proto"; + +package OstProto; + +// 802.2 LLC +message Dot2Llc { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Llc dot2Llc = 206; +} diff --git a/common/dot2llcconfig.h b/common/dot2llcconfig.h new file mode 100644 index 0000000..76a5b24 --- /dev/null +++ b/common/dot2llcconfig.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _DOT2_LLC_CONFIG_H +#define _DOT2_LLC_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "dot3config.h" +#include "llcconfig.h" +#include "dot3.h" +#include "llc.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2LlcFieldNumber, + Dot3ConfigForm, LlcConfigForm, + Dot3Protocol, LlcProtocol + > Dot2LlcConfigForm; + +#endif diff --git a/common/dot2snap.h b/common/dot2snap.h new file mode 100644 index 0000000..0da586a --- /dev/null +++ b/common/dot2snap.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_H +#define _DOT2_SNAP_H + +#include "comboprotocol.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocol Dot2SnapProtocol; + +#endif diff --git a/common/dot2snap.proto b/common/dot2snap.proto new file mode 100644 index 0000000..d49059f --- /dev/null +++ b/common/dot2snap.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.2 SNAP +message Dot2Snap { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Dot2Snap dot2Snap = 207; +} diff --git a/common/dot2snapconfig.h b/common/dot2snapconfig.h new file mode 100644 index 0000000..32a8d99 --- /dev/null +++ b/common/dot2snapconfig.h @@ -0,0 +1,36 @@ +/* +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 +*/ + +#ifndef _DOT2_SNAP_CONFIG_H +#define _DOT2_SNAP_CONFIG_H + +#include "comboprotocol.h" + +#include "dot2llcconfig.h" +#include "snapconfig.h" +#include "dot2llc.h" +#include "snap.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kDot2SnapFieldNumber, + Dot2LlcConfigForm, SnapConfigForm, + Dot2LlcProtocol, SnapProtocol + > Dot2SnapConfigForm; + +#endif diff --git a/common/dot3.cpp b/common/dot3.cpp new file mode 100644 index 0000000..68ef9a6 --- /dev/null +++ b/common/dot3.cpp @@ -0,0 +1,196 @@ +/* +Copyright (C) 2010-2014 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 "dot3.h" + +Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Dot3Protocol::~Dot3Protocol() +{ +} + +AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Dot3Protocol(stream, parent); +} + +quint32 Dot3Protocol::protocolNumber() const +{ + return OstProto::Protocol::kDot3FieldNumber; +} + +void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::dot3)) + data.MergeFrom(protocol.GetExtension(OstProto::dot3)); +} + +QString Dot3Protocol::name() const +{ + return QString("802.3"); +} + +QString Dot3Protocol::shortName() const +{ + return QString("802.3"); +} + +int Dot3Protocol::fieldCount() const +{ + return dot3_fieldCount; +} + +AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case dot3_length: + break; + + // Meta fields + case dot3_is_override_length: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case dot3_length: + switch(attrib) + { + case FieldName: + return QString("Length"); + case FieldValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + return len; + } + case FieldTextValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + + return QString("%1").arg(len); + } + case FieldFrameValue: + { + quint16 len = data.is_override_length() ? + data.length() : protocolFramePayloadSize(streamIndex); + QByteArray fv; + + fv.resize(2); + qToBigEndian(len, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + // Meta fields + case dot3_is_override_length: + { + switch(attrib) + { + case FieldValue: + return data.is_override_length(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Dot3Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case dot3_length: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_length(len); + break; + } + case dot3_is_override_length: + { + bool ovr = value.toBool(); + data.set_is_override_length(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool Dot3Protocol::isProtocolFrameValueVariable() const +{ + return isProtocolFramePayloadSizeVariable(); +} + +int Dot3Protocol::protocolFrameVariableCount() const +{ + return protocolFramePayloadVariableCount(); +} diff --git a/common/dot3.h b/common/dot3.h new file mode 100644 index 0000000..ecade93 --- /dev/null +++ b/common/dot3.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _DOT3_H +#define _DOT3_H + +#include "abstractprotocol.h" +#include "dot3.pb.h" + +class Dot3Protocol : public AbstractProtocol +{ +public: + enum Dot3field + { + dot3_length, + + // Meta-fields + dot3_is_override_length, + + dot3_fieldCount + }; + + Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Dot3Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Dot3 data; +}; + +#endif diff --git a/common/dot3.proto b/common/dot3.proto new file mode 100644 index 0000000..f20f120 --- /dev/null +++ b/common/dot3.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// 802.3 +message Dot3 { + optional bool is_override_length = 2; + optional uint32 length = 1; +} + +extend Protocol { + optional Dot3 dot3 = 201; +} diff --git a/common/dot3.ui b/common/dot3.ui new file mode 100644 index 0000000..5631eaf --- /dev/null +++ b/common/dot3.ui @@ -0,0 +1,86 @@ + + dot3 + + + + 0 + 0 + 181 + 98 + + + + Form + + + + + + 802.3 + + + + + + Length + + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 16 + 54 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideLength + toggled(bool) + leLength + setEnabled(bool) + + + 55 + 39 + + + 84 + 43 + + + + + diff --git a/common/dot3config.cpp b/common/dot3config.cpp new file mode 100644 index 0000000..d17094e --- /dev/null +++ b/common/dot3config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010-2014 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 "dot3config.h" +#include "dot3.h" +#include + +Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + leLength->setValidator(new QIntValidator(0, 16384, this)); +} + +Dot3ConfigForm::~Dot3ConfigForm() +{ +} + +Dot3ConfigForm* Dot3ConfigForm::createInstance() +{ + return new Dot3ConfigForm; +} + +void Dot3ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideLength->setChecked( + proto->fieldData( + Dot3Protocol::dot3_is_override_length, + AbstractProtocol::FieldValue + ).toBool()); + leLength->setText( + proto->fieldData( + Dot3Protocol::dot3_length, + AbstractProtocol::FieldValue + ).toString()); +} + +void Dot3ConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + Dot3Protocol::dot3_is_override_length, + cbOverrideLength->isChecked()); + proto->setFieldData( + Dot3Protocol::dot3_length, + leLength->text()); +} + diff --git a/common/dot3config.h b/common/dot3config.h new file mode 100644 index 0000000..44e3e7e --- /dev/null +++ b/common/dot3config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _DOT3_CONFIG_H +#define _DOT3_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_dot3.h" + +class Dot3ConfigForm : + public AbstractProtocolConfigForm, + private Ui::dot3 +{ + Q_OBJECT +public: + Dot3ConfigForm(QWidget *parent = 0); + virtual ~Dot3ConfigForm(); + + static Dot3ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/eth2.cpp b/common/eth2.cpp new file mode 100644 index 0000000..63f361a --- /dev/null +++ b/common/eth2.cpp @@ -0,0 +1,188 @@ +/* +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 +*/ + +#include "eth2.h" + +Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Eth2Protocol::~Eth2Protocol() +{ +} + +AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Eth2Protocol(stream, parent); +} + +quint32 Eth2Protocol::protocolNumber() const +{ + return OstProto::Protocol::kEth2FieldNumber; +} + +void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::eth2)) + data.MergeFrom(protocol.GetExtension(OstProto::eth2)); +} + +QString Eth2Protocol::name() const +{ + return QString("Ethernet II"); +} + +QString Eth2Protocol::shortName() const +{ + return QString("Eth II"); +} + +AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +int Eth2Protocol::fieldCount() const +{ + return eth2_fieldCount; +} + +AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case eth2_type: + break; + + case eth2_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case eth2_type: + { + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + } + case FieldTextValue: + { + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + QByteArray fv; + quint16 type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + fv.resize(2); + qToBigEndian((quint16) type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case eth2_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Eth2Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case eth2_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case eth2_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} diff --git a/common/eth2.h b/common/eth2.h new file mode 100644 index 0000000..5846d14 --- /dev/null +++ b/common/eth2.h @@ -0,0 +1,66 @@ +/* +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 +*/ + +#ifndef _ETH2_H +#define _ETH2_H + +#include "abstractprotocol.h" + +#include "eth2.pb.h" + + +class Eth2Protocol : public AbstractProtocol +{ +public: + enum eth2field + { + eth2_type = 0, + + eth2_is_override_type, + + eth2_fieldCount + }; + + Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Eth2Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); +private: + OstProto::Eth2 data; +}; + +#endif diff --git a/common/eth2.proto b/common/eth2.proto new file mode 100644 index 0000000..47db7e7 --- /dev/null +++ b/common/eth2.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet II +message Eth2 { + optional bool is_override_type = 2; + + optional uint32 type = 1; +} + +extend Protocol { + optional Eth2 eth2 = 200; +} diff --git a/common/eth2.ui b/common/eth2.ui new file mode 100644 index 0000000..a43fb36 --- /dev/null +++ b/common/eth2.ui @@ -0,0 +1,80 @@ + + eth2 + + + + 0 + 0 + 190 + 64 + + + + Form + + + + + + Ethernet Type + + + + + + + false + + + >HH HH; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 98 + 27 + + + 118 + 27 + + + + + diff --git a/common/eth2config.cpp b/common/eth2config.cpp new file mode 100644 index 0000000..7cf3bd5 --- /dev/null +++ b/common/eth2config.cpp @@ -0,0 +1,63 @@ +/* +Copyright (C) 2010,2014 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 "eth2config.h" +#include "eth2.h" + +Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +Eth2ConfigForm::~Eth2ConfigForm() +{ +} + +Eth2ConfigForm* Eth2ConfigForm::createInstance() +{ + return new Eth2ConfigForm; +} + +void Eth2ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideType->setChecked( + proto->fieldData( + Eth2Protocol::eth2_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + Eth2Protocol::eth2_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void Eth2ConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + Eth2Protocol::eth2_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + Eth2Protocol::eth2_type, + leType->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/eth2config.h b/common/eth2config.h new file mode 100644 index 0000000..918a855 --- /dev/null +++ b/common/eth2config.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2010,2014 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 +*/ + +#ifndef _ETH2_CONFIG_H +#define _ETH2_CONFIG_H + +#include "abstractprotocolconfig.h" + +#include "eth2.pb.h" +#include "ui_eth2.h" + +class Eth2ConfigForm : + public AbstractProtocolConfigForm, + public Ui::eth2 +{ + Q_OBJECT +public: + Eth2ConfigForm(QWidget *parent = 0); + virtual ~Eth2ConfigForm(); + + static Eth2ConfigForm* createInstance(); + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; +#endif diff --git a/common/eth2pdml.cpp b/common/eth2pdml.cpp new file mode 100644 index 0000000..8bb8d17 --- /dev/null +++ b/common/eth2pdml.cpp @@ -0,0 +1,124 @@ +/* +Copyright (C) 2011 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 "eth2pdml.h" + +#include "dot3.pb.h" +#include "eth2.pb.h" +#include "mac.pb.h" +#include "vlan.pb.h" + +PdmlEthProtocol::PdmlEthProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMacFieldNumber; + + fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); + fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); +} + +PdmlProtocol* PdmlEthProtocol::createInstance() +{ + return new PdmlEthProtocol(); +} + +void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "eth.vlan.tpid") + { + bool isOk; + + uint tpid = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kVlanFieldNumber); + + OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(tpid); + vlan->set_is_override_tpid(true); + } + else if (name == "eth.vlan.id") + { + bool isOk; + + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + OstProto::Vlan *vlan = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::vlan); + + vlan->set_vlan_tag(tag); + } + else if (name == "eth.type") + { + bool isOk; + + uint type = attributes.value("value").toString() + .toUInt(&isOk, kBaseHex); + + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(type); + eth2->set_is_override_type(true); + } + else if (name == "eth.len") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kDot3FieldNumber); + + OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); + + bool isOk; + dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + dot3->set_is_override_length(true); + } +#if 0 + else if (name == "eth.trailer") + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } + else if ((name == "eth.fcs") || + attributes.value("show").toString().startsWith("Frame check sequence")) + { + QByteArray trailer = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + stream->mutable_core()->mutable_name()->append(trailer.constData(), + trailer.size()); + } +#endif +} + diff --git a/common/eth2pdml.h b/common/eth2pdml.h new file mode 100644 index 0000000..b776147 --- /dev/null +++ b/common/eth2pdml.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ETH2_PDML_H +#define _ETH2_PDML_H + +#include "pdmlprotocol.h" + +class PdmlEthProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlEthProtocol(); +}; + +#endif diff --git a/common/fileformat.cpp b/common/fileformat.cpp new file mode 100644 index 0000000..4edd980 --- /dev/null +++ b/common/fileformat.cpp @@ -0,0 +1,483 @@ +/* +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 +*/ + +#include "fileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#include + +const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +FileFormat fileFormat; + +const int kBaseHex = 16; + +FileFormat::FileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +FileFormat::~FileFormat() +{ +} + +bool FileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + if (!content.matter().has_streams()) + goto _missing_streams; + + postParseFixup(meta.data(), content); + + streams.CopyFrom(content.matter().streams()); + + return true; + +_missing_streams: + error = QString(tr("%1 does not contain any streams")).arg(fileName); + goto _fail; +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.matter().InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.matter().DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileContent content; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); + Q_ASSERT(meta.IsInitialized()); + + if (!streams.IsInitialized()) + goto _stream_not_init; + + content.mutable_matter()->mutable_streams()->CopyFrom(streams); + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_stream_not_init: + error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) + .arg(QString().fromStdString( + streams.InitializationErrorString())) + .arg(QString().fromStdString(streams.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool FileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + +_close_exit: + file.close(); +_exit: + return ret; +} + +bool FileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("Ostinato")) + return true; + else + return false; +} + +void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void FileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/fileformat.h b/common/fileformat.h new file mode 100644 index 0000000..409b8a8 --- /dev/null +++ b/common/fileformat.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#ifndef _FILE_FORMAT_H +#define _FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include "fileformat.pb.h" + +class FileFormat : public AbstractFileFormat +{ +public: + FileFormat(); + ~FileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 2; + static const uint kFileFormatVersionRevision = 3; +}; + +extern FileFormat fileFormat; + +#endif diff --git a/common/fileformat.proto b/common/fileformat.proto new file mode 100644 index 0000000..ce2a688 --- /dev/null +++ b/common/fileformat.proto @@ -0,0 +1,98 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +enum FileType { + kReservedFileType = 0; + kStreamsFileType = 1; +} + +message FileMetaData { + required FileType file_type = 1; + required uint32 format_version_major = 2; + required uint32 format_version_minor = 3; + required uint32 format_version_revision = 4; + required string generator_name = 5; + required string generator_version = 6; + required string generator_revision = 7; +} + +message FileContentMatter { + optional StreamConfigList streams = 1; +} + +/* + An Ostinato file is the binary encoding of the File message below + STRICTLY in increasing order of field number for the top level fields + + We do not use field number '1' for magic value because its encoded key + is '0a' (LF or \n) which occurs commonly in text files. Checksum should + be the last field, so top level field numbers greater than 15 are not + permitted. We use 15 as the checksum field number because it is the + largest field number that can fit in a 1-byte tag + + The magic value is of a fixed length so that meta data has a fixed offset + from the start in the encoded message. + + Checksum is fixed length so that it is at a fixed negative offset from + the end in the encoded message. + + Because the protobuf serialization API does not _guarantee_ + strict ordering, so we define wrapper messages for each top level field + and serialize the individual wrapper messages. The field numbers MUST + be the same in 'File' and the wrapper messages +*/ +message File { + // FieldNumber 1 is reserved and MUST not be used! + required bytes magic_value = 2; + required FileMetaData meta_data = 3; + optional FileContent content_matter = 9; + required fixed32 checksum_value = 15; +} + +/* + The magic value is 10 bytes - "\xa7\xb7OSTINATO" + The 1st-2nd byte has the MSB set to avoid mixup with text files + The 3rd-10th byte spell OSTINATO (duh!) + + Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes + Encoded Value: 120aa7b7 4f535449 4e41544f +*/ +message FileMagic { + required bytes value = 2; +} + +message FileMeta { + required FileMetaData data = 3; +} + +message FileContent { + optional FileContentMatter matter = 9; +} + +/* + Encoded Size : Key(1) + Value(4) = 5 bytes + Encoded Value: 7d xxXXxxXX +*/ +message FileChecksum { + required fixed32 value = 15; // should always be a fixed 32-bit size +} diff --git a/common/gmp.cpp b/common/gmp.cpp new file mode 100755 index 0000000..240650d --- /dev/null +++ b/common/gmp.cpp @@ -0,0 +1,769 @@ +/* +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 +*/ + +#include "gmp.h" +#include + +QHash GmpProtocol::frameFieldCountMap; + +GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +GmpProtocol::~GmpProtocol() +{ +} + +AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +int GmpProtocol::fieldCount() const +{ + return FIELD_COUNT; +} + +int GmpProtocol::frameFieldCount() const +{ + int type = msgType(); + + // frameFieldCountMap contains the frameFieldCounts for each + // msgType - this is built on demand and cached for subsequent use + + // lookup if we have already cached ... + if (frameFieldCountMap.contains(type)) + return frameFieldCountMap.value(type); + + // ... otherwise calculate and cache + int count = 0; + for (int i = 0; i < FIELD_COUNT; i++) + { + if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) + count++; + } + frameFieldCountMap.insert(type, count); + return count; +} + +AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + flags &= ~FrameField; + + switch(index) + { + // Frame Fields - check against msgType() + case kType: + case kRsvdMrtCode: + flags |= FrameField; + break; + case kChecksum: + flags |= FrameField; + flags |= CksumField; + break; + case kMldMrt: + case kMldRsvd: + // MLD subclass should handle suitably + break; + + case kGroupAddress: + if (!isSsmReport()) + flags |= FrameField; + break; + + case kRsvd1: + case kSFlag: + case kQrv: + case kQqic: + case kSourceCount: + case kSources: + if (isSsmQuery()) + flags |= FrameField; + break; + + case kRsvd2: + case kGroupRecordCount: + case kGroupRecords: + if (isSsmReport()) + flags |= FrameField; + break; + + // Meta Fields + case kIsOverrideChecksum: + case kGroupMode: + case kGroupCount: + case kGroupPrefix: + case kIsOverrideSourceCount: + case kIsOverrideGroupRecordCount: + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kType: + { + uint type = data.type(); + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg(quint8(type)); + case FieldFrameValue: + return QByteArray(1, quint8(type)); + default: + break; + } + break; + } + case kRsvdMrtCode: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, rsvd); + default: + break; + } + break; + } + case kChecksum: + { + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldBitSize: + return 16; + default: + break; + } + + quint16 cksum = data.is_override_checksum() ? + data.checksum() : checksum(streamIndex); + + switch(attrib) + { + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + default: + break; + } + break; + } + case kMldMrt: + case kMldRsvd: + // XXX: Present only in MLD - hence handled by the mld subclass + break; + + case kGroupAddress: + // XXX: Handled by each subclass + break; + + case kRsvd1: + { + quint8 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + return QByteArray(1, char(rsvd)); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case kSFlag: + { + switch(attrib) + { + case FieldName: + return QString("S Flag"); + case FieldValue: + return data.s_flag(); + case FieldTextValue: + return data.s_flag() ? QString("True") : QString("False"); + case FieldFrameValue: + return QByteArray(1, char(data.s_flag())); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + case kQrv: + { + int qrv = data.qrv() & 0x7; + + switch(attrib) + { + case FieldName: + return QString("QRV"); + case FieldValue: + return qrv; + case FieldTextValue: + return QString("%1").arg(qrv); + case FieldFrameValue: + return QByteArray(1, char(qrv)); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + case kQqic: + { + int qqi = data.qqi(); + + switch(attrib) + { + case FieldName: + return QString("QQIC"); + case FieldValue: + return qqi; + case FieldTextValue: + return QString("%1").arg(qqi); + case FieldFrameValue: + { + char qqicode = char(qqic(qqi)); + return QByteArray(1, qqicode); + } + default: + break; + } + break; + } + case kSourceCount: + { + quint16 count = data.sources_size(); + + if (data.is_override_source_count()) + count = data.source_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Sources"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + // XXX: Handled by each subclass + break; + case kRsvd2: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecordCount: + { + quint16 count = data.group_records_size(); + + if (data.is_override_group_record_count()) + count = data.group_record_count(); + + switch(attrib) + { + case FieldName: + return QString("Number of Group Records"); + case FieldValue: + return count; + case FieldTextValue: + return QString("%1").arg(count); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(count, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldName: + return QString("Group List"); + case FieldValue: + { + QVariantList grpRecords; + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec; + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordType"] = rec.type(); + // grpRec["groupRecordAddress"] = subclass responsibility + grpRec["overrideGroupRecordSourceCount"] = + rec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = rec.source_count(); + + // grpRec["groupRecordSourceList"] = subclass responsibility + grpRec["overrideAuxDataLength"] = + rec.is_override_aux_data_length(); + grpRec["auxDataLength"] = rec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString::fromStdString(rec.aux_data())); + + grpRecords.append(grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList fv; + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv; + quint16 srcCount; + + rv.resize(4); + rv[0] = rec.type(); + rv[1] = rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4; + + if (rec.is_override_source_count()) + srcCount = rec.source_count(); + else + srcCount = rec.sources_size(); + qToBigEndian(srcCount, (uchar*)(rv.data()+2)); + + // group_address => subclass responsibility + // source list => subclass responsibility + + rv.append(QString().fromStdString(rec.aux_data())); + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString str; + + str.append(" Type: "); + switch(rec.type()) + { + case OstProto::Gmp::GroupRecord::kIsInclude: + str.append("IS_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kIsExclude: + str.append("IS_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToInclude: + str.append("TO_INCLUDE"); break; + case OstProto::Gmp::GroupRecord::kToExclude: + str.append("TO_EXCLUDE"); break; + case OstProto::Gmp::GroupRecord::kAllowNew: + str.append("ALLOW_NEW"); break; + case OstProto::Gmp::GroupRecord::kBlockOld: + str.append("BLOCK_OLD"); break; + default: + str.append("UNKNOWN"); break; + } + str.append(QString("; AuxLen: %1").arg( + rec.is_override_aux_data_length() ? + rec.aux_data_length() : rec.aux_data().size()/4)); + str.append(QString("; Source Count: %1").arg( + rec.is_override_source_count() ? + rec.source_count(): rec.sources_size())); + + // NOTE: subclass should replace the XXX below with + // group address and source list + str.append(QString("; XXX")); + + str.append(QString("; AuxData: ").append( + QByteArray().append(QString().fromStdString( + rec.aux_data())).toHex())); + + list.append(str); + } + return list; + } + default: + break; + } + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + switch(attrib) + { + case FieldValue: return data.is_override_checksum(); + default: break; + } + break; + } + case kGroupMode: + { + switch(attrib) + { + case FieldValue: return data.group_mode(); + default: break; + } + break; + } + case kGroupCount: + { + switch(attrib) + { + case FieldValue: return data.group_count(); + default: break; + } + break; + } + case kGroupPrefix: + { + switch(attrib) + { + case FieldValue: return data.group_prefix(); + default: break; + } + break; + } + case kIsOverrideSourceCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_source_count(); + default: break; + } + break; + } + case kIsOverrideGroupRecordCount: + { + switch(attrib) + { + case FieldValue: return data.is_override_group_record_count(); + default: break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool GmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kType: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case kRsvdMrtCode: + { + uint val = value.toUInt(&isOk); + if (isOk) + data.set_rsvd_code(val); + break; + } + case kChecksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case kMldMrt: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd1: + isOk = false; + break; + case kSFlag: + { + bool flag = value.toBool(); + data.set_s_flag(flag); + isOk = true; + break; + } + case kQrv: + { + uint qrv = value.toUInt(&isOk); + if (isOk) + data.set_qrv(qrv); + break; + } + case kQqic: + { + uint qqi = value.toUInt(&isOk); + if (isOk) + data.set_qqi(qqi); + break; + } + case kSourceCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_source_count(count); + break; + } + case kSources: + // XXX: Handled by subclass + isOk = false; + break; + case kRsvd2: + isOk = false; + break; + case kGroupRecordCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_record_count(count); + break; + } + case kGroupRecords: + { + QVariantList list = value.toList(); + + data.clear_group_records(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.add_group_records(); + + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + grpRec["groupRecordType"].toInt())); + // NOTE: rec->group_address => subclass responsibility + rec->set_is_override_source_count( + grpRec["overrideGroupRecordSourceCount"].toBool()); + rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); + // NOTE: rec->sources => subclass responsibility + rec->set_is_override_aux_data_length( + grpRec["overrideAuxDataLength"].toBool()); + rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); + QByteArray ba = grpRec["auxData"].toByteArray(); + // pad to word boundary + if (ba.size() % 4) + ba.append(QByteArray(4 - (ba.size() % 4), char(0))); + rec->set_aux_data(std::string(ba.constData(), ba.size())); + } + + break; + } + + // Meta Fields + case kIsOverrideChecksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + + case kGroupMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.GroupMode_IsValid(mode)) + data.set_group_mode((OstProto::Gmp::GroupMode)mode); + break; + } + case kGroupCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_group_count(count); + break; + } + case kGroupPrefix: + { + uint prefix = value.toUInt(&isOk); + if (isOk) + data.set_group_prefix(prefix); + break; + } + + case kIsOverrideSourceCount: + { + bool ovr = value.toBool(); + data.set_is_override_source_count(ovr); + isOk = true; + break; + } + + case kIsOverrideGroupRecordCount: + { + bool ovr = value.toBool(); + data.set_is_override_group_record_count(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int GmpProtocol::protocolFrameSize(int streamIndex) const +{ + // TODO: Calculate to reduce processing cost + return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); +} + +bool GmpProtocol::isProtocolFrameValueVariable() const +{ + // No fields vary for Ssm Report + if (isSsmReport()) + return false; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + return true; + + return false; +} + +int GmpProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + // No fields vary for Ssm Report + if (isSsmReport()) + return count; + + // For all other msg types, check the group mode + if (fieldData(kGroupMode, FieldValue).toUInt() + != uint(OstProto::Gmp::kFixed)) + { + count = AbstractProtocol::lcm(count, + fieldData(kGroupCount, FieldValue).toUInt()); + } + + return count; +} diff --git a/common/gmp.h b/common/gmp.h new file mode 100755 index 0000000..a293c1f --- /dev/null +++ b/common/gmp.h @@ -0,0 +1,129 @@ +/* +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 +*/ + +#ifndef _GMP_H +#define _GMP_H + +#include "abstractprotocol.h" +#include "gmp.pb.h" + +#include + +/* +Gmp Protocol Frame Format - TODO: for now see the respective RFCs +*/ + +class GmpProtocol : public AbstractProtocol +{ +public: + enum GmpField + { + // ------------ + // Frame Fields + // ------------ + // Fields used in all ASM and SSM messages, unless otherwise specified + kType = 0, + kRsvdMrtCode, + kChecksum, + kMldMrt, // MLD Only (except MLDv2 Report) + kMldRsvd, // MLD Only (except MLDv2 Report) + + // Field used in ASM messages + kGroupAddress, + FIELD_COUNT_ASM_ALL, + + // Fields used in SSM Query + kRsvd1 = FIELD_COUNT_ASM_ALL, + kSFlag, + kQrv, + kQqic, + kSourceCount, + kSources, + FIELD_COUNT_SSM_QUERY, + + // Fields used in SSM Report + kRsvd2 = FIELD_COUNT_SSM_QUERY, + kGroupRecordCount, + kGroupRecords, + FIELD_COUNT_SSM_REPORT, + FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, + + // ----------- + // Meta Fields + // ----------- + kIsOverrideChecksum = FRAME_FIELD_COUNT, + + kGroupMode, + kGroupCount, + kGroupPrefix, + + kIsOverrideSourceCount, + + kIsOverrideGroupRecordCount, + + FIELD_COUNT + }; + + GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~GmpProtocol(); + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +protected: + OstProto::Gmp data; + + int msgType() const; + + virtual bool isSsmReport() const = 0; + virtual bool isQuery() const = 0; + virtual bool isSsmQuery() const = 0; + + int qqic(int value) const; + + virtual quint16 checksum(int streamIndex) const = 0; + +private: + static QHash frameFieldCountMap; +}; + +inline int GmpProtocol::msgType() const +{ + return fieldData(kType, FieldValue).toInt(); +} + +inline int GmpProtocol::qqic(int value) const +{ + return quint8(value); // TODO: if value > 128 convert to mantissa/exp form +} + +#endif diff --git a/common/gmp.proto b/common/gmp.proto new file mode 100755 index 0000000..f1fbf56 --- /dev/null +++ b/common/gmp.proto @@ -0,0 +1,94 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Group Management Protocol (i.e. IGMP and MLD) +message Gmp { + // + // Common fields for both ASM and SSM messages + // + optional uint32 type = 1; + optional bool is_override_rsvd_code = 2; + optional uint32 rsvd_code = 3; + // MaxRespTime is in milliseconds - MaxRespCode will be derived + optional uint32 max_response_time = 4 [default = 100]; + optional bool is_override_checksum = 5; + optional uint32 checksum = 6; + + message IpAddress { + optional fixed32 v4 = 1; + optional fixed64 v6_hi = 2; + optional fixed64 v6_lo = 3; + } + + // + // Fields used in ASM messages + // + enum GroupMode { + kFixed = 0; + kIncrementGroup = 1; + kDecrementGroup = 2; + kRandomGroup = 3; + } + optional IpAddress group_address = 10; + optional GroupMode group_mode = 11 [default = kFixed]; + optional uint32 group_count = 12 [default = 16]; + optional uint32 group_prefix = 13 [default = 24]; + + // + // Fields used in SSM Query + // + optional bool s_flag = 20; + optional uint32 qrv = 21 [default = 2]; + // QuerierQueryInterval is in seconds - QQIC will be derived + optional uint32 qqi = 22 [default = 125]; + repeated IpAddress sources = 23; + optional bool is_override_source_count = 24; + optional uint32 source_count = 25; + + // + // Fields used in SSM Reports + // + message GroupRecord { + enum RecordType { + kReserved = 0; + kIsInclude = 1; + kIsExclude = 2; + kToInclude = 3; + kToExclude = 4; + kAllowNew = 5; + kBlockOld = 6; + } + + optional RecordType type = 1 [default = kIsInclude]; + optional IpAddress group_address = 2; + repeated IpAddress sources = 3; + optional bool is_override_source_count = 4; + optional uint32 source_count = 5; + optional bytes aux_data = 6; + optional bool is_override_aux_data_length = 7; + optional uint32 aux_data_length = 8; + } + repeated GroupRecord group_records = 30; + optional bool is_override_group_record_count = 31; + optional uint32 group_record_count = 32; +} diff --git a/common/gmp.ui b/common/gmp.ui new file mode 100755 index 0000000..6260af6 --- /dev/null +++ b/common/gmp.ui @@ -0,0 +1,835 @@ + + Gmp + + + + 0 + 0 + 509 + 355 + + + + Form + + + + + + + + Message Type + + + msgTypeCombo + + + + + + + + + + Max Response Time (1/10s) + + + maxResponseTime + + + + + + + + 0 + 0 + + + + + + + + Checksum + + + + + + + false + + + + 0 + 0 + + + + >HHHH; + + + + + + + + + + + + + + + Group Address + + + groupAddress + + + + + + + Mode + + + msgTypeCombo + + + + + + + Count + + + msgTypeCombo + + + + + + + Prefix + + + msgTypeCombo + + + + + + + + 1 + 0 + + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + /900; + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + S Flag (Suppress Router Processing) + + + + + + + QRV + + + qrv + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QQI + + + qqi + + + + + + + + + + Qt::Vertical + + + + 61 + 41 + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Count + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + Group Records + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 16 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Groups + + + + + + + false + + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Record Type + + + groupRecordType + + + + + + + + Reserved + + + + + Is Include + + + + + Is Exclude + + + + + To Include + + + + + To Exclude + + + + + Allow New + + + + + Block Old + + + + + + + + Group Address + + + groupRecordAddress + + + + + + + + + + + + + + + + Source List + + + groupRecordAddress + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + + + + + + + + + – + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + + + + + + + Number of Sources + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + + + + + + + Aux Data + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length (x4) + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 101 + 21 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + msgTypeCombo + maxResponseTime + overrideChecksum + checksum + groupAddress + groupMode + groupCount + groupPrefix + overrideGroupRecordCount + groupRecordCount + groupRecordType + groupRecordAddress + overrideGroupRecordSourceCount + groupRecordSourceCount + overrideAuxDataLength + auxDataLength + auxData + sFlag + qrv + qqi + overrideSourceCount + sourceCount + + + + + overrideChecksum + toggled(bool) + checksum + setEnabled(bool) + + + 391 + 43 + + + 448 + 45 + + + + + overrideGroupRecordSourceCount + toggled(bool) + groupRecordSourceCount + setEnabled(bool) + + + 402 + 202 + + + 436 + 204 + + + + + overrideAuxDataLength + toggled(bool) + auxDataLength + setEnabled(bool) + + + 416 + 286 + + + 433 + 286 + + + + + overrideGroupRecordCount + toggled(bool) + groupRecordCount + setEnabled(bool) + + + 112 + 309 + + + 138 + 312 + + + + + overrideSourceCount + toggled(bool) + sourceCount + setEnabled(bool) + + + 413 + 154 + + + 434 + 151 + + + + +
    diff --git a/common/gmpconfig.cpp b/common/gmpconfig.cpp new file mode 100644 index 0000000..ee6af11 --- /dev/null +++ b/common/gmpconfig.cpp @@ -0,0 +1,384 @@ +/* +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 +*/ + +#include "gmpconfig.h" +#include "gmp.h" + +GmpConfigForm::GmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + auxData->setValidator(new QRegExpValidator( + QRegExp("[0-9A-Fa-f]*"), this)); +} + +GmpConfigForm::~GmpConfigForm() +{ +} + +void GmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + msgTypeCombo->setValue( + proto->fieldData( + GmpProtocol::kType, + AbstractProtocol::FieldValue + ).toUInt()); + + // XXX: maxResponseTime set by subclass + + overrideChecksum->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideChecksum, + AbstractProtocol::FieldValue + ).toBool()); + checksum->setText(uintToHexStr( + proto->fieldData( + GmpProtocol::kChecksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + groupAddress->setText( + proto->fieldData( + GmpProtocol::kGroupAddress, + AbstractProtocol::FieldValue + ).toString()); + groupMode->setCurrentIndex( + proto->fieldData( + GmpProtocol::kGroupMode, + AbstractProtocol::FieldValue + ).toUInt()); + groupCount->setText( + proto->fieldData( + GmpProtocol::kGroupCount, + AbstractProtocol::FieldValue + ).toString()); + groupPrefix->setText( + proto->fieldData( + GmpProtocol::kGroupPrefix, + AbstractProtocol::FieldValue + ).toString()); + + sFlag->setChecked( + proto->fieldData( + GmpProtocol::kSFlag, + AbstractProtocol::FieldValue + ).toBool()); + qrv->setText( + proto->fieldData( + GmpProtocol::kQrv, + AbstractProtocol::FieldValue + ).toString()); + qqi->setText( + proto->fieldData( + GmpProtocol::kQqic, + AbstractProtocol::FieldValue + ).toString()); + + QStringList sl = + proto->fieldData( + GmpProtocol::kSources, + AbstractProtocol::FieldValue + ).toStringList(); + sourceList->clear(); + foreach(QString src, sl) + { + QListWidgetItem *item = new QListWidgetItem(src); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->addItem(item); + } + + // NOTE: SourceCount should be loaded after sourceList + overrideSourceCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideSourceCount, + AbstractProtocol::FieldValue + ).toBool()); + sourceCount->setText( + proto->fieldData( + GmpProtocol::kSourceCount, + AbstractProtocol::FieldValue + ).toString()); + + QVariantList list = + proto->fieldData( + GmpProtocol::kGroupRecords, + AbstractProtocol::FieldValue + ).toList(); + groupList->clear(); + foreach (QVariant rec, list) + { + QVariantMap grpRec = rec.toMap(); + QListWidgetItem *item = new QListWidgetItem; + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText( + grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + groupList->addItem(item); + } + + // NOTE: recordCount should be loaded after recordList + overrideGroupRecordCount->setChecked( + proto->fieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + AbstractProtocol::FieldValue + ).toBool()); + groupRecordCount->setText( + proto->fieldData( + GmpProtocol::kGroupRecordCount, + AbstractProtocol::FieldValue + ).toString()); +} + +void GmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + update(); + + proto->setFieldData( + GmpProtocol::kType, + msgTypeCombo->currentValue()); + + // XXX: maxResponseTime handled by subclass + + proto->setFieldData( + GmpProtocol::kIsOverrideChecksum, + overrideChecksum->isChecked()); + proto->setFieldData( + GmpProtocol::kChecksum, + hexStrToUInt(checksum->text())); + + proto->setFieldData( + GmpProtocol::kGroupAddress, + groupAddress->text()); + proto->setFieldData( + GmpProtocol::kGroupMode, + groupMode->currentIndex()); + proto->setFieldData( + GmpProtocol::kGroupCount, + groupCount->text()); + proto->setFieldData( + GmpProtocol::kGroupPrefix, + groupPrefix->text().remove('/')); + + proto->setFieldData( + GmpProtocol::kSFlag, + sFlag->isChecked()); + proto->setFieldData( + GmpProtocol::kQrv, + qrv->text()); + proto->setFieldData( + GmpProtocol::kQqic, + qqi->text()); + + QStringList list; + for (int i = 0; i < sourceList->count(); i++) + list.append(sourceList->item(i)->text()); + + proto->setFieldData( + GmpProtocol::kSources, + list); + + // sourceCount should be AFTER sources + proto->setFieldData( + GmpProtocol::kIsOverrideSourceCount, + overrideSourceCount->isChecked()); + proto->setFieldData( + GmpProtocol::kSourceCount, + sourceCount->text()); + + QVariantList grpList; + for (int i = 0; i < groupList->count(); i++) + { + QVariant grp = groupList->item(i)->data(Qt::UserRole); + grpList.append(grp.toMap()); + } + proto->setFieldData(GmpProtocol::kGroupRecords, grpList); + + // groupRecordCount should be AFTER groupRecords + proto->setFieldData( + GmpProtocol::kIsOverrideGroupRecordCount, + overrideGroupRecordCount->isChecked()); + proto->setFieldData( + GmpProtocol::kGroupRecordCount, + groupRecordCount->text()); +} + +void GmpConfigForm::update() +{ + // save the current group Record by simulating a currentItemChanged() + on_groupList_currentItemChanged(groupList->currentItem(), + groupList->currentItem()); +} + +// +// -- private slots +// + +void GmpConfigForm::on_groupMode_currentIndexChanged(int index) +{ + bool disabled = (index == 0); + + groupCount->setDisabled(disabled); + groupPrefix->setDisabled(disabled); +} + +void GmpConfigForm::on_addSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + sourceList->insertItem(sourceList->currentRow(), item); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_deleteSource_clicked() +{ + delete sourceList->takeItem(sourceList->currentRow()); + + if (!overrideSourceCount->isChecked()) + sourceCount->setText(QString().setNum(sourceList->count())); +} + +void GmpConfigForm::on_addGroupRecord_clicked() +{ + OstProto::Gmp::GroupRecord defRec; + QVariantMap grpRec; + QListWidgetItem *item = new QListWidgetItem; + + grpRec["groupRecordType"] = defRec.type(); + grpRec["groupRecordAddress"] = _defaultGroupIp; + grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); + grpRec["groupRecordSourceCount"] = defRec.source_count(); + grpRec["groupRecordSourceList"] = QStringList(); + grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); + grpRec["auxDataLength"] = defRec.aux_data_length(); + grpRec["auxData"] = QByteArray().append( + QString().fromStdString(defRec.aux_data())); + + item->setData(Qt::UserRole, grpRec); + item->setText(QString("%1: %2") + .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) + .arg(grpRec["groupRecordAddress"].toString())); + + groupList->insertItem(groupList->currentRow(), item); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_deleteGroupRecord_clicked() +{ + delete groupList->takeItem(groupList->currentRow()); + + if (!overrideGroupRecordCount->isChecked()) + groupRecordCount->setText(QString().setNum(groupList->count())); +} + +void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous) +{ + QVariantMap rec; + QStringList strList; + + qDebug("in %s", __FUNCTION__); + + // save previous record ... + if (previous == NULL) + goto _load_current_record; + + rec["groupRecordType"] = groupRecordType->currentIndex(); + rec["groupRecordAddress"] = groupRecordAddress->text(); + strList.clear(); + while (groupRecordSourceList->count()) + { + QListWidgetItem *item = groupRecordSourceList->takeItem(0); + strList.append(item->text()); + delete item; + } + rec["groupRecordSourceList"] = strList; + rec["overrideGroupRecordSourceCount"] = + overrideGroupRecordSourceCount->isChecked(); + rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); + rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); + rec["auxDataLength"] = auxDataLength->text().toUInt(); + rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); + + previous->setData(Qt::UserRole, rec); + previous->setText(QString("%1: %2") + .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) + .arg(rec["groupRecordAddress"].toString())); + +_load_current_record: + // ... and load current record + if (current == NULL) + goto _exit; + + rec = current->data(Qt::UserRole).toMap(); + + groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); + groupRecordAddress->setText(rec["groupRecordAddress"].toString()); + strList = rec["groupRecordSourceList"].toStringList(); + groupRecordSourceList->clear(); + foreach (QString str, strList) + { + QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + overrideGroupRecordSourceCount->setChecked( + rec["overrideGroupRecordSourceCount"].toBool()); + groupRecordSourceCount->setText(QString().setNum( + rec["groupRecordSourceCount"].toUInt())); + overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); + auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); + auxData->setText(QString(rec["auxData"].toByteArray().toHex())); + +_exit: + groupRecord->setEnabled(current != NULL); + return; +} + +void GmpConfigForm::on_addGroupRecordSource_clicked() +{ + QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); + item->setFlags(item->flags() | Qt::ItemIsEditable); + groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_deleteGroupRecordSource_clicked() +{ + delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); + + if (!overrideGroupRecordSourceCount->isChecked()) + groupRecordSourceCount->setText(QString().setNum( + groupRecordSourceList->count())); +} + +void GmpConfigForm::on_auxData_textChanged(const QString &text) +{ + // auxDataLength is in units of words and each byte is 2 chars in text() + if (!overrideAuxDataLength->isChecked()) + auxDataLength->setText(QString().setNum((text.size()+7)/8)); +} diff --git a/common/gmpconfig.h b/common/gmpconfig.h new file mode 100644 index 0000000..70a833b --- /dev/null +++ b/common/gmpconfig.h @@ -0,0 +1,63 @@ +/* +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 +*/ + +#ifndef _GMP_CONFIG_H +#define _GMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_gmp.h" + +class GmpConfigForm : + public AbstractProtocolConfigForm, + protected Ui::Gmp +{ + Q_OBJECT +public: + GmpConfigForm(QWidget *parent = 0); + ~GmpConfigForm(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +protected: + QString _defaultGroupIp; + QString _defaultSourceIp; + enum { + kSsmQueryPage = 0, + kSsmReportPage = 1 + }; + +private: + void update(); + +private slots: + void on_groupMode_currentIndexChanged(int index); + void on_addSource_clicked(); + void on_deleteSource_clicked(); + + void on_addGroupRecord_clicked(); + void on_deleteGroupRecord_clicked(); + void on_groupList_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *previous); + void on_addGroupRecordSource_clicked(); + void on_deleteGroupRecordSource_clicked(); + void on_auxData_textChanged(const QString &text); +}; + +#endif diff --git a/common/hexdump.cpp b/common/hexdump.cpp new file mode 100644 index 0000000..376ccf0 --- /dev/null +++ b/common/hexdump.cpp @@ -0,0 +1,211 @@ +/* +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 +*/ + +#include "hexdump.h" +#include "streambase.h" + +HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +HexDumpProtocol::~HexDumpProtocol() +{ +} + +AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new HexDumpProtocol(stream, parent); +} + +quint32 HexDumpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kHexDumpFieldNumber; +} + +void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::hexDump)) + data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); +} + +QString HexDumpProtocol::name() const +{ + return QString("HexDump"); +} + +QString HexDumpProtocol::shortName() const +{ + return QString("HexDump"); +} + +int HexDumpProtocol::fieldCount() const +{ + return hexDump_fieldCount; +} + +AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case hexDump_content: + flags |= FrameField; + break; + + case hexDump_pad_until_end: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case hexDump_content: + { + QByteArray ba; + QByteArray pad; + + switch(attrib) + { + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + ba.append(QString().fromStdString(data.content())); + if (data.pad_until_end()) + { + pad = QByteArray( + protocolFrameSize(streamIndex) - ba.size(), '\0'); + } + break; + + default: + break; + } + + switch(attrib) + { + case FieldName: + return QString("Content"); + case FieldValue: + return ba; + case FieldTextValue: + return ba.append(pad).toHex(); + case FieldFrameValue: + return ba.append(pad); + default: + break; + } + break; + + } + + // Meta fields + case hexDump_pad_until_end: + { + switch(attrib) + { + case FieldValue: + return data.pad_until_end(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool HexDumpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case hexDump_content: + { + QByteArray ba = value.toByteArray(); + data.set_content(ba.constData(), ba.size()); + isOk = true; + break; + } + case hexDump_pad_until_end: + { + bool pad = value.toBool(); + data.set_pad_until_end(pad); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int HexDumpProtocol::protocolFrameSize(int streamIndex) const +{ + int len = data.content().size(); + + if (data.pad_until_end()) + { + int pad = mpStream->frameLen(streamIndex) + - (protocolFrameOffset(streamIndex) + len + kFcsSize); + if (pad < 0) + pad = 0; + len += pad; + } + + return len; +} + diff --git a/common/hexdump.h b/common/hexdump.h new file mode 100644 index 0000000..4318192 --- /dev/null +++ b/common/hexdump.h @@ -0,0 +1,73 @@ +/* +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 +*/ + +#ifndef _HEXDUMP_H +#define _HEXDUMP_H + +#include "abstractprotocol.h" +#include "hexdump.pb.h" + +/* +HexDump Protocol Frame Format - + +---------+---------+ + | User | Zero | + | HexDump | Padding | + +---------+---------+ +*/ + +class HexDumpProtocol : public AbstractProtocol +{ +public: + enum hexDumpfield + { + // Frame Fields + hexDump_content = 0, + + // Meta Fields + hexDump_pad_until_end, + + hexDump_fieldCount + }; + HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~HexDumpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + +private: + OstProto::HexDump data; +}; +#endif diff --git a/common/hexdump.proto b/common/hexdump.proto new file mode 100644 index 0000000..6cdc3d5 --- /dev/null +++ b/common/hexdump.proto @@ -0,0 +1,32 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// HexDump Protocol +message HexDump { + optional bytes content = 1; + optional bool pad_until_end = 2 [default = true]; +} + +extend Protocol { + optional HexDump hexDump = 104; +} diff --git a/common/hexdump.ui b/common/hexdump.ui new file mode 100644 index 0000000..61f187a --- /dev/null +++ b/common/hexdump.ui @@ -0,0 +1,76 @@ + + HexDump + + + + 0 + 0 + 511 + 190 + + + + Form + + + + + + + + + Pad until end of packet + + + + + + + Qt::Horizontal + + + + 281 + 20 + + + + + + + + + 50 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + QHexEdit + QWidget +
    qhexedit.h
    + 1 +
    +
    + + +
    diff --git a/common/hexdumpconfig.cpp b/common/hexdumpconfig.cpp new file mode 100644 index 0000000..1c057b6 --- /dev/null +++ b/common/hexdumpconfig.cpp @@ -0,0 +1,75 @@ +/* +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 +*/ + +#include "hexdumpconfig.h" +#include "hexdump.h" + +HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + hexEdit->setFont(QFont("Courier")); + hexEdit->setOverwriteMode(false); +} + +HexDumpConfigForm::~HexDumpConfigForm() +{ +} + +HexDumpConfigForm* HexDumpConfigForm::createInstance() +{ + return new HexDumpConfigForm; +} + +void HexDumpConfigForm::loadWidget(AbstractProtocol *proto) +{ + hexEdit->setData( + proto->fieldData( + HexDumpProtocol::hexDump_content, + AbstractProtocol::FieldValue + ).toByteArray()); + padUntilEnd->setChecked( + proto->fieldData( + HexDumpProtocol::hexDump_pad_until_end, + AbstractProtocol::FieldValue + ).toBool()); +} + +void HexDumpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + HexDumpProtocol::hexDump_content, + hexEdit->data()); + proto->setFieldData( + HexDumpProtocol::hexDump_pad_until_end, + padUntilEnd->isChecked()); +} + +// +// ------------ private slots +// +void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) +{ + if (isOverwriteMode) + mode->setText(tr("Ovr")); + else + mode->setText(tr("Ins")); +} + diff --git a/common/hexdumpconfig.h b/common/hexdumpconfig.h new file mode 100644 index 0000000..b0dcfa7 --- /dev/null +++ b/common/hexdumpconfig.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _HEX_DUMP_CONFIG_H +#define _HEX_DUMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_hexdump.h" + +class HexDumpConfigForm : + public AbstractProtocolConfigForm, + private Ui::HexDump +{ + Q_OBJECT +public: + HexDumpConfigForm(QWidget *parent = 0); + virtual ~HexDumpConfigForm(); + + static HexDumpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); +}; + +#endif diff --git a/common/icmp.cpp b/common/icmp.cpp new file mode 100644 index 0000000..651efb5 --- /dev/null +++ b/common/icmp.cpp @@ -0,0 +1,396 @@ +/* +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 +*/ + +#include "icmp.h" +#include "icmphelper.h" + +IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +IcmpProtocol::~IcmpProtocol() +{ +} + +AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IcmpProtocol(stream, parent); +} + +quint32 IcmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIcmpFieldNumber; +} + +void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::icmp)) + data.MergeFrom(protocol.GetExtension(OstProto::icmp)); +} + +QString IcmpProtocol::name() const +{ + return QString("Internet Control Message Protocol"); +} + +QString IcmpProtocol::shortName() const +{ + return QString("ICMP"); +} + +quint32 IcmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: + switch(icmpVersion()) + { + case OstProto::Icmp::kIcmp4: return 0x1; + case OstProto::Icmp::kIcmp6: return 0x3A; + default:break; + } + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int IcmpProtocol::fieldCount() const +{ + return icmp_fieldCount; +} + +int IcmpProtocol::frameFieldCount() const +{ + int count; + + if (isIdSeqType(icmpVersion(), icmpType())) + count = icmp_idSeqFrameFieldCount; + else + count = icmp_commonFrameFieldCount; + + return count; + +} + +AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case icmp_type: + case icmp_code: + break; + + case icmp_checksum: + flags |= CksumField; + break; + + case icmp_identifier: + case icmp_sequence: + if (!isIdSeqType(icmpVersion(), icmpType())) + flags &= ~FrameField; + break; + + case icmp_version: + case icmp_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case icmp_type: + { + unsigned char type = data.type() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + return type; + case FieldTextValue: + return QString("%1").arg((uint) type); + case FieldFrameValue: + return QByteArray(1, type); + default: + break; + } + break; + + } + case icmp_code: + { + unsigned char code = data.code() & 0xFF; + + switch(attrib) + { + case FieldName: + return QString("Code"); + case FieldValue: + return code; + case FieldTextValue: + return QString("%1").arg((uint)code); + case FieldFrameValue: + return QByteArray(1, code); + default: + break; + } + break; + + } + case icmp_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + { + cksum = data.checksum(); + } + else + { + quint16 cks; + quint32 sum = 0; + + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + if (icmpVersion() == OstProto::Icmp::kIcmp6) + { + cks = protocolFrameHeaderCksum(streamIndex, + CksumIpPseudo); + sum += (quint16) ~cks; + } + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cksum = (~sum) & 0xFFFF; + } + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case icmp_identifier: + { + switch(attrib) + { + case FieldName: + return QString("Identifier"); + case FieldValue: + return data.identifier(); + case FieldTextValue: + return QString("%1").arg(data.identifier()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.identifier(), + (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case icmp_sequence: + { + switch(attrib) + { + case FieldName: + return QString("Sequence"); + case FieldValue: + return data.sequence(); + case FieldTextValue: + return QString("%1").arg(data.sequence()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case icmp_version: + { + switch(attrib) + { + case FieldValue: + return data.icmp_version(); + default: + break; + } + break; + } + case icmp_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool IcmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case icmp_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type & 0xFF); + break; + } + case icmp_code: + { + uint code = value.toUInt(&isOk); + if (isOk) + data.set_code(code & 0xFF); + break; + } + case icmp_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case icmp_identifier: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_identifier(id); + break; + } + case icmp_sequence: + { + uint seq = value.toUInt(&isOk); + if (isOk) + data.set_sequence(seq); + break; + } + case icmp_version: + { + int ver = value.toUInt(&isOk); + if (isOk) + data.set_icmp_version(OstProto::Icmp::Version(ver)); + break; + } + case icmp_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + + + diff --git a/common/icmp.h b/common/icmp.h new file mode 100644 index 0000000..b24f025 --- /dev/null +++ b/common/icmp.h @@ -0,0 +1,96 @@ +/* +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 +*/ + +#ifndef _ICMP_H +#define _ICMP_H + +#include "abstractprotocol.h" +#include "icmp.pb.h" + +/* +Icmp Protocol Frame Format - + +-----+------+------+------+-------+ + | TYP | CODE | CSUM | [ID] | [SEQ] | + | (1) | (1) | (2) | (2) | (2) | + +-----+------+------+------+-------+ +Fields within [] are applicable only to certain TYPEs +Figures in braces represent field width in bytes +*/ + +class IcmpProtocol : public AbstractProtocol +{ +public: + enum icmpfield + { + // Frame Fields + icmp_type = 0, + icmp_code, + icmp_checksum, + icmp_commonFrameFieldCount, + + icmp_identifier = icmp_commonFrameFieldCount, + icmp_sequence, + icmp_idSeqFrameFieldCount, + + // Meta Fields + icmp_is_override_checksum = icmp_idSeqFrameFieldCount, + icmp_version, + + icmp_fieldCount + }; + + IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IcmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Icmp data; + + OstProto::Icmp::Version icmpVersion() const + { + return OstProto::Icmp::Version( + fieldData(icmp_version, FieldValue).toUInt()); + } + int icmpType() const + { + return fieldData(icmp_type, FieldValue).toInt(); + } +}; + +#endif diff --git a/common/icmp.proto b/common/icmp.proto new file mode 100644 index 0000000..6abc686 --- /dev/null +++ b/common/icmp.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Icmp Protocol +message Icmp { + + enum Version { + kIcmp4 = 4; + kIcmp6 = 6; + } + + optional Version icmp_version = 1 [default = kIcmp4]; + optional bool is_override_checksum = 2; + + optional uint32 type = 6 [default = 0x8]; // echo request + optional uint32 code = 7; + optional uint32 checksum = 8; + optional uint32 identifier = 9 [default = 1234]; + optional uint32 sequence = 10; +} + +extend Protocol { + optional Icmp icmp = 402; +} diff --git a/common/icmp.ui b/common/icmp.ui new file mode 100644 index 0000000..7ba1938 --- /dev/null +++ b/common/icmp.ui @@ -0,0 +1,178 @@ + + Icmp + + + + 0 + 0 + 373 + 166 + + + + Form + + + + + + Version + + + + + + ICMPv4 + + + + + + + ICMPv6 + + + + + + + + + + Type + + + typeCombo + + + + + + + + + + Code + + + codeEdit + + + + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + Checksum + + + + + + + false + + + + + + + + + + + + + Identifier + + + idEdit + + + + + + + + + + Sequence + + + seqEdit + + + + + + + + + + + + + Qt::Vertical + + + + 211 + 71 + + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + icmp4Button + icmp6Button + typeCombo + codeEdit + overrideCksum + cksumEdit + idEdit + seqEdit + + + + + overrideCksum + toggled(bool) + cksumEdit + setEnabled(bool) + + + 33 + 70 + + + 96 + 71 + + + + +
    diff --git a/common/icmp6pdml.cpp b/common/icmp6pdml.cpp new file mode 100644 index 0000000..53c4a34 --- /dev/null +++ b/common/icmp6pdml.cpp @@ -0,0 +1,100 @@ +/* +Copyright (C) 2011 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 "icmp6pdml.h" + +#include "icmp.pb.h" +#include "sample.pb.h" + +PdmlIcmp6Protocol::PdmlIcmp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + proto_ = NULL; +} + +PdmlProtocol* PdmlIcmp6Protocol::createInstance() +{ + return new PdmlIcmp6Protocol(); +} + +void PdmlIcmp6Protocol::preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, + int expectedPos, OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); + mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); +} + +void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + if (proto_) + proto_->postProtocolHandler(pbProto, stream); + else + stream->mutable_protocol()->RemoveLast(); + + proto_ = NULL; + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; +} + +void PdmlIcmp6Protocol::unknownFieldHandler(QString name, + int pos, int size, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (proto_) + { + proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, + stream); + } + else if (name == "icmpv6.type") + { + bool isOk; + uint type = attributes.value("value").toString().toUInt( + &isOk, kBaseHex); + + if (((type >= 130) && (type <= 132)) || (type == 143)) + { + // MLD + proto_ = &mld_; + fieldMap_ = mld_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + } + else + { + // ICMP + proto_ = &icmp_; + fieldMap_ = icmp_.fieldMap_; + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + } + + pbProto->mutable_protocol_id()->set_id(ostProtoId_); + pbProto->MutableExtension(OstProto::sample)->Clear(); + + fieldHandler(name, attributes, pbProto, stream); + } + else + { + qDebug("unexpected field %s", name.toAscii().constData()); + } +} + diff --git a/common/icmp6pdml.h b/common/icmp6pdml.h new file mode 100644 index 0000000..d159824 --- /dev/null +++ b/common/icmp6pdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP6_PDML_H +#define _ICMP6_PDML_H + +#include "pdmlprotocol.h" + +#include "icmppdml.h" +#include "mldpdml.h" + +class PdmlIcmp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlIcmp6Protocol(); +private: + PdmlIcmpProtocol icmp_; + PdmlMldProtocol mld_; + PdmlProtocol *proto_; +}; + +#endif diff --git a/common/icmpconfig.cpp b/common/icmpconfig.cpp new file mode 100644 index 0000000..2bf65af --- /dev/null +++ b/common/icmpconfig.cpp @@ -0,0 +1,191 @@ +/* +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 +*/ + +#include "icmpconfig.h" + +#include "icmp.h" +#include "icmphelper.h" + +#include + +IcmpConfigForm::IcmpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + versionGroup = new QButtonGroup(this); + setupUi(this); + + // auto-connect's not working, for some reason I can't figure out! + // slot name changed to when_ instead of on_ so that connectSlotsByName() + // doesn't complain + connect(versionGroup, + SIGNAL(buttonClicked(int)), + SLOT(when_versionGroup_buttonClicked(int))); + + versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); + versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); + + typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); + + icmp4Button->click(); + + idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); + seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); +} + +IcmpConfigForm::~IcmpConfigForm() +{ +} + +IcmpConfigForm* IcmpConfigForm::createInstance() +{ + return new IcmpConfigForm; +} + +void IcmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + versionGroup->button( + proto->fieldData( + IcmpProtocol::icmp_version, + AbstractProtocol::FieldValue + ).toUInt())->click(); + + typeCombo->setValue( + proto->fieldData( + IcmpProtocol::icmp_type, + AbstractProtocol::FieldValue + ).toUInt()); + codeEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_code, + AbstractProtocol::FieldValue + ).toString()); + + overrideCksum->setChecked( + proto->fieldData( + IcmpProtocol::icmp_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + cksumEdit->setText(uintToHexStr( + proto->fieldData( + IcmpProtocol::icmp_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + idEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_identifier, + AbstractProtocol::FieldValue + ).toString()); + seqEdit->setText( + proto->fieldData( + IcmpProtocol::icmp_sequence, + AbstractProtocol::FieldValue + ).toString()); +} + +void IcmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + IcmpProtocol::icmp_version, + versionGroup->checkedId()); + + proto->setFieldData( + IcmpProtocol::icmp_type, + typeCombo->currentValue()); + proto->setFieldData( + IcmpProtocol::icmp_code, + codeEdit->text()); + + proto->setFieldData( + IcmpProtocol::icmp_is_override_checksum, + overrideCksum->isChecked()); + proto->setFieldData( + IcmpProtocol::icmp_checksum, + hexStrToUInt(cksumEdit->text())); + + proto->setFieldData( + IcmpProtocol::icmp_identifier, + idEdit->text()); + proto->setFieldData( + IcmpProtocol::icmp_sequence, + seqEdit->text()); +} + +// +// -------- private slots +// +void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) +{ + idSeqFrame->setVisible( + isIdSeqType( + OstProto::Icmp::Version(versionGroup->checkedId()), + typeCombo->currentValue())); +} + +void IcmpConfigForm::when_versionGroup_buttonClicked(int id) +{ + int value = typeCombo->currentValue(); + + typeCombo->clear(); + + switch(id) + { + case OstProto::Icmp::kIcmp4: + typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); + typeCombo->addItem(kIcmpDestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); + typeCombo->addItem(kIcmpRedirect, "Redirect"); + typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); + typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); + typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); + typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); + typeCombo->addItem(kIcmpInformationRequest, "Information Request"); + typeCombo->addItem(kIcmpInformationReply, "Information Reply"); + typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); + typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); + break; + + case OstProto::Icmp::kIcmp6: + typeCombo->addItem(kIcmp6DestinationUnreachable, + "Destination Unreachable"); + typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); + typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); + typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); + + typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); + typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); + typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); + typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); + typeCombo->addItem(kIcmp6NeighbourSolicitation, + "Neighbour Solicitation"); + typeCombo->addItem(kIcmp6NeighbourAdvertisement, + "Neighbour Advertisement"); + typeCombo->addItem(kIcmp6Redirect, "Redirect"); + typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); + typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); + break; + default: + Q_ASSERT(false); + } + + typeCombo->setValue(value); +} + diff --git a/common/icmpconfig.h b/common/icmpconfig.h new file mode 100644 index 0000000..6e01065 --- /dev/null +++ b/common/icmpconfig.h @@ -0,0 +1,50 @@ +/* +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 +*/ + +#ifndef _ICMP_CONFIG_H +#define _ICMP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_icmp.h" + +class QButtonGroup; + +class IcmpConfigForm : + public AbstractProtocolConfigForm, + private Ui::Icmp +{ + Q_OBJECT +public: + IcmpConfigForm(QWidget *parent = 0); + virtual ~IcmpConfigForm(); + + static IcmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + QButtonGroup *versionGroup; + +private slots: + void on_typeCombo_currentIndexChanged(int index); + void when_versionGroup_buttonClicked(int id); +}; + +#endif diff --git a/common/icmphelper.h b/common/icmphelper.h new file mode 100644 index 0000000..0c5bcf7 --- /dev/null +++ b/common/icmphelper.h @@ -0,0 +1,88 @@ +/* +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 +*/ + +#ifndef _ICMP_HELPER_H +#define _ICMP_HELPER_H + +#include "icmp.pb.h" + +#include + +enum IcmpType +{ + kIcmpEchoReply = 0, + kIcmpDestinationUnreachable = 3, + kIcmpSourceQuench = 4, + kIcmpRedirect = 5, + kIcmpEchoRequest = 8, + kIcmpTimeExceeded = 11, + kIcmpParameterProblem = 12, + kIcmpTimestampRequest = 13, + kIcmpTimestampReply = 14, + kIcmpInformationRequest = 15, + kIcmpInformationReply = 16, + kIcmpAddressMaskRequest = 17, + kIcmpAddressMaskReply = 18 +}; + +enum Icmp6Type +{ + kIcmp6DestinationUnreachable = 1, + kIcmp6PacketTooBig = 2, + kIcmp6TimeExceeded = 3, + kIcmp6ParameterProblem = 4, + kIcmp6EchoRequest = 128, + kIcmp6EchoReply = 129, + kIcmp6RouterSolicitation = 133, + kIcmp6RouterAdvertisement = 134, + kIcmp6NeighbourSolicitation = 135, + kIcmp6NeighbourAdvertisement = 136, + kIcmp6Redirect = 137, + kIcmp6InformationQuery = 139, + kIcmp6InformationResponse = 140 +}; + +static QSet icmpIdSeqSet = QSet() + << kIcmpEchoRequest + << kIcmpEchoReply + << kIcmpInformationRequest + << kIcmpInformationReply; + +static QSet icmp6IdSeqSet = QSet() + << kIcmp6EchoRequest + << kIcmp6EchoReply; + +bool inline isIdSeqType(OstProto::Icmp::Version ver, int type) +{ + //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); + switch(ver) + { + case OstProto::Icmp::kIcmp4: + return icmpIdSeqSet.contains(type); + case OstProto::Icmp::kIcmp6: + return icmp6IdSeqSet.contains(type); + default: + break; + } + + Q_ASSERT(false); // unreachable + return false; +} + +#endif diff --git a/common/icmppdml.cpp b/common/icmppdml.cpp new file mode 100644 index 0000000..0bff798 --- /dev/null +++ b/common/icmppdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "icmppdml.h" + +#include "icmp.pb.h" + +PdmlIcmpProtocol::PdmlIcmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; + + fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); + + fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); + fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.echo.identifier", + OstProto::Icmp::kIdentifierFieldNumber); + fieldMap_.insert("icmpv6.echo.sequence_number", + OstProto::Icmp::kSequenceFieldNumber); +} + +PdmlProtocol* PdmlIcmpProtocol::createInstance() +{ + return new PdmlIcmpProtocol(); +} + +void PdmlIcmpProtocol::preProtocolHandler(QString name, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (name == "icmp") + icmp->set_icmp_version(OstProto::Icmp::kIcmp4); + else if (name == "icmpv6") + icmp->set_icmp_version(OstProto::Icmp::kIcmp6); + + icmp->set_is_override_checksum(true); + + icmp->set_type(kIcmpInvalidType); +} + +void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) + && (icmp->type() >= kIcmp6EchoRequest) + && (icmp->type() <= kIcmp6EchoReply)) + { + QString addrHexStr = attributes.value("value").toString(); + + // Wireshark 1.4.x does not have these as filterable fields + if (attributes.value("show").toString().startsWith("ID")) + icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); + else if (attributes.value("show").toString().startsWith("Sequence")) + icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); + } +} + +void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); + + if (icmp->type() == kIcmpInvalidType) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/icmppdml.h b/common/icmppdml.h new file mode 100644 index 0000000..58b3e37 --- /dev/null +++ b/common/icmppdml.h @@ -0,0 +1,48 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _ICMP_PDML_H +#define _ICMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIcmpProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIcmpProtocol(); +private: + static const uint kIcmpInvalidType = 0xFFFFFFFF; + + static const uint kIcmp6EchoRequest = 128; + static const uint kIcmp6EchoReply = 129; +}; + +#endif diff --git a/common/igmp.cpp b/common/igmp.cpp new file mode 100644 index 0000000..8a5a0b9 --- /dev/null +++ b/common/igmp.cpp @@ -0,0 +1,364 @@ +/* +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 +*/ + +#include "igmp.h" +#include "iputils.h" + +#include +#include + +IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kIgmpV2Query); +} + +IgmpProtocol::~IgmpProtocol() +{ +} + +AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new IgmpProtocol(stream, parent); +} + +quint32 IgmpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kIgmpFieldNumber; +} + +void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::igmp)) + data.MergeFrom(protocol.GetExtension(OstProto::igmp)); +} + +QString IgmpProtocol::name() const +{ + return QString("Internet Group Management Protocol"); +} + +QString IgmpProtocol::shortName() const +{ + return QString("IGMP"); +} + +quint32 IgmpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x2; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = 0; + quint8 mrcode = 0; + + if (msgType() == kIgmpV3Query) + { + mrt = data.max_response_time(); + mrcode = quint8(mrc(mrt)); + } + else if (msgType() == kIgmpV2Query) + { + mrt = data.max_response_time(); + mrcode = mrt & 0xFF; + } + + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + else + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1").arg(mrt); + case FieldFrameValue: + return QByteArray(1, mrcode); + default: + break; + } + break; + } + case kGroupAddress: + { + quint32 grpIp = ipUtils::ipAddress( + data.group_address().v4(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + return QHostAddress(grpIp).toString(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(grpIp, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); + return fv; + } + case FieldTextValue: + { + QStringList list; + + for (int i = 0; i < data.sources_size(); i++) + list.append(QHostAddress(data.sources(i).v4()).toString()); + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + grpRec["groupRecordAddress"] = QHostAddress( + rec.group_address().v4()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v4(), + (uchar*)(rv.data()+4)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v4(), + (uchar*)(rv.data()+8+4*j)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + str.append(QString("Group: %1").arg( + QHostAddress(rec.group_address().v4()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + sl.append(QHostAddress(rec.sources(j).v4()).toString()); + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool IgmpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kRsvdMrtCode: + { + uint mrt = value.toUInt(&isOk); + if (isOk) + data.set_max_response_time(mrt); + break; + } + case kGroupAddress: + { + QHostAddress addr(value.toString()); + quint32 ip = addr.toIPv4Address(); + isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); + if (isOk) + data.mutable_group_address()->set_v4(ip); + break; + } + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + quint32 ip = QHostAddress(str).toIPv4Address(); + data.add_sources()->set_v4(ip); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + + rec->mutable_group_address()->set_v4(QHostAddress( + grpRec["groupRecordAddress"].toString()) + .toIPv4Address()); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString src, srcList) + { + rec->add_sources()->set_v4( + QHostAddress(src).toIPv4Address()); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +quint16 IgmpProtocol::checksum(int streamIndex) const +{ + quint16 cks; + quint32 sum = 0; + + // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto + cks = protocolFrameCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + cks = protocolFramePayloadCksum(streamIndex, CksumIp); + sum += (quint16) ~cks; + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + cks = (~sum) & 0xFFFF; + + return cks; +} diff --git a/common/igmp.h b/common/igmp.h new file mode 100644 index 0000000..0634a1f --- /dev/null +++ b/common/igmp.h @@ -0,0 +1,98 @@ +/* +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 +*/ +#ifndef _IGMP_H +#define _IGMP_H + +#include "gmp.h" +#include "igmp.pb.h" + +// IGMP uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum IgmpMsgType +{ + kIgmpV1Query = 0x11, + kIgmpV1Report = 0x12, + + kIgmpV2Query = 0xFF11, + kIgmpV2Report = 0x16, + kIgmpV2Leave = 0x17, + + kIgmpV3Query = 0xFE11, + kIgmpV3Report = 0x22, +}; + +class IgmpProtocol : public GmpProtocol +{ +public: + IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~IgmpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool IgmpProtocol::isSsmReport() const +{ + return (msgType() == kIgmpV3Report); +} + +inline bool IgmpProtocol::isQuery() const +{ + return ((msgType() == kIgmpV1Query) + || (msgType() == kIgmpV2Query) + || (msgType() == kIgmpV3Query)); +} + +inline bool IgmpProtocol::isSsmQuery() const +{ + return (msgType() == kIgmpV3Query); +} + +inline int IgmpProtocol::mrc(int value) const +{ + return quint8(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/igmp.proto b/common/igmp.proto new file mode 100755 index 0000000..a6f005c --- /dev/null +++ b/common/igmp.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp igmp = 403; +} diff --git a/common/igmpconfig.cpp b/common/igmpconfig.cpp new file mode 100644 index 0000000..743e9a3 --- /dev/null +++ b/common/igmpconfig.cpp @@ -0,0 +1,112 @@ +/* +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 +*/ + +#include "igmpconfig.h" +#include "igmp.h" +#include "ipv4addressdelegate.h" + +IgmpConfigForm::IgmpConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); + msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); + msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); + msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); + msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); + msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); + msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); + + _defaultGroupIp = "0.0.0.0"; + _defaultSourceIp = "0.0.0.0"; + + groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator + groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator + sourceList->setItemDelegate(new IPv4AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); +} + +IgmpConfigForm::~IgmpConfigForm() +{ +} + +IgmpConfigForm* IgmpConfigForm::createInstance() +{ + return new IgmpConfigForm; +} + +void IgmpConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + IgmpProtocol::kRsvdMrtCode, + AbstractProtocol::FieldValue + ).toString()); +} + +void IgmpConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + IgmpProtocol::kRsvdMrtCode, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kIgmpV1Query: + case kIgmpV1Report: + case kIgmpV2Query: + case kIgmpV2Report: + case kIgmpV2Leave: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kIgmpV3Query: + asmGroup->show(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kIgmpV3Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} + diff --git a/common/igmpconfig.h b/common/igmpconfig.h new file mode 100644 index 0000000..6428db1 --- /dev/null +++ b/common/igmpconfig.h @@ -0,0 +1,40 @@ +/* +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 +*/ +#ifndef _IGMP_CONFIG_H +#define _IGMP_CONFIG_H + +#include "gmpconfig.h" + +class IgmpConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + IgmpConfigForm(QWidget *parent = 0); + virtual ~IgmpConfigForm(); + + static IgmpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/igmppdml.cpp b/common/igmppdml.cpp new file mode 100644 index 0000000..19516d7 --- /dev/null +++ b/common/igmppdml.cpp @@ -0,0 +1,141 @@ +/* +Copyright (C) 2011 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 "igmppdml.h" + +#include "igmp.pb.h" + +PdmlIgmpProtocol::PdmlIgmpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; + + fieldMap_.insert("igmp.max_resp", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); + + fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME + + fieldMap_.insert("igmp.num_grp_recs", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlIgmpProtocol::createInstance() +{ + return new PdmlIgmpProtocol(); +} + +void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + + igmp->set_is_override_rsvd_code(true); + igmp->set_is_override_checksum(true); + igmp->set_is_override_source_count(true); + igmp->set_is_override_group_record_count(true); + + version_ = 0; +} + +void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "igmp.version") + { + version_ = attributes.value("show").toString().toUInt(&isOk); + } + else if (name == "igmp.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + if (type == kIgmpQuery) + { + switch(version_) + { + case 1: type = kIgmpV1Query; break; + case 2: type = kIgmpV2Query; break; + case 3: type = kIgmpV3Query; break; + } + } + igmp->set_type(type); + } + else if (name == "igmp.record_type") + { + OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "igmp.aux_data_len") + { + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.num_src") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.maddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->mutable_group_address()->set_v4( + valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.saddr") + { + if (igmp->group_record_count()) + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + else + igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "igmp.aux_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + igmp->mutable_group_records(igmp->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + +void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream *stream) +{ + // version is 0 for IGMP like protocols such as RGMP which we don't + // support currently + if (version_ == 0) + stream->mutable_protocol()->RemoveLast(); +} + diff --git a/common/igmppdml.h b/common/igmppdml.h new file mode 100644 index 0000000..4b553a7 --- /dev/null +++ b/common/igmppdml.h @@ -0,0 +1,49 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IGMP_PDML_H +#define _IGMP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIgmpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIgmpProtocol(); +private: + static const uint kIgmpQuery = 0x11; + static const uint kIgmpV1Query = 0x11; + static const uint kIgmpV2Query = 0xFF11; + static const uint kIgmpV3Query = 0xFE11; + + uint version_; +}; + +#endif diff --git a/common/intcombobox.h b/common/intcombobox.h new file mode 100644 index 0000000..f52bdef --- /dev/null +++ b/common/intcombobox.h @@ -0,0 +1,69 @@ +/* +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 +*/ + +#ifndef __INT_COMBO_BOX +#define __INT_COMBO_BOX + +#include + +class IntComboBox : public QComboBox +{ +public: + IntComboBox(QWidget *parent = 0) + : QComboBox(parent) + { + valueMask_ = 0xFFFFFFFF; + setEditable(true); + } + void addItem(int value, const QString &text) + { + QComboBox::addItem( + QString("%1 - %2").arg(value & valueMask_).arg(text), + value); + } + int currentValue() + { + bool isOk; + int index = findText(currentText()); + if (index >= 0) + return itemData(index).toInt(); + else + return currentText().toInt(&isOk, 0); + } + void setValue(int value) + { + int index = findData(value); + if (index >= 0) + setCurrentIndex(index); + else + setEditText(QString().setNum(value)); + } + uint valueMask() + { + return valueMask_; + } + void setValueMask(uint mask) + { + valueMask_ = mask; + } +private: + uint valueMask_; +}; + +#endif diff --git a/common/ip4.cpp b/common/ip4.cpp new file mode 100644 index 0000000..1c1557b --- /dev/null +++ b/common/ip4.cpp @@ -0,0 +1,846 @@ +/* +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 +*/ + +#include "ip4.h" + +#include + +Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +Ip4Protocol::~Ip4Protocol() +{ +} + +AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new Ip4Protocol(stream, parent); +} + +quint32 Ip4Protocol::protocolNumber() const +{ + return OstProto::Protocol::kIp4FieldNumber; +} + +void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::ip4)) + data.MergeFrom(protocol.GetExtension(OstProto::ip4)); +} + +QString Ip4Protocol::name() const +{ + return QString("Internet Protocol ver 4"); +} + +QString Ip4Protocol::shortName() const +{ + return QString("IPv4"); +} + +AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +quint32 Ip4Protocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0x060603; + case ProtocolIdEth: return 0x0800; + case ProtocolIdIp: return 0x04; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int Ip4Protocol::fieldCount() const +{ + return ip4_fieldCount; +} + +AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case ip4_ver: + case ip4_hdrLen: + case ip4_tos: + case ip4_totLen: + case ip4_id: + case ip4_flags: + case ip4_fragOfs: + case ip4_ttl: + case ip4_proto: + break; + + case ip4_cksum: + flags |= CksumField; + break; + + case ip4_srcAddr: + case ip4_dstAddr: + break; + + case ip4_isOverrideVer: + case ip4_isOverrideHdrLen: + case ip4_isOverrideTotLen: + case ip4_isOverrideProto: + case ip4_isOverrideCksum: + case ip4_srcAddrMode: + case ip4_srcAddrCount: + case ip4_srcAddrMask: + case ip4_dstAddrMode: + case ip4_dstAddrCount: + case ip4_dstAddrMask: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case ip4_ver: + { + int ver; + + ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; + + switch(attrib) + { + case FieldName: + return QString("Version"); + case FieldValue: + return ver; + case FieldTextValue: + return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) ver); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_hdrLen: + { + int hdrlen; + + hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() & 0x0F : 5; + + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + return hdrlen; + case FieldTextValue: + return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char) hdrlen); + case FieldBitSize: + return 4; + default: + break; + } + break; + } + case ip4_tos: + switch(attrib) + { + case FieldName: + return QString("TOS/DSCP"); + case FieldValue: + return data.tos(); + case FieldFrameValue: + return QByteArray(1, (char) data.tos()); + case FieldTextValue: + return QString("0x%1"). + arg(data.tos(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_totLen: + { + switch(attrib) + { + case FieldName: + return QString("Total Length"); + case FieldValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? data.totlen() : + (protocolFramePayloadSize(streamIndex) + 20); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_id: + switch(attrib) + { + case FieldName: + return QString("Identification"); + case FieldValue: + return data.id(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.id(), (uchar*)fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(data.id(), 2, BASE_HEX, QChar('0'));; + default: + break; + } + break; + case ip4_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return data.flags(); + case FieldFrameValue: + return QByteArray(1, (char) data.flags()); + case FieldTextValue: + { + QString s; + s.append("Unused:"); + s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); + s.append(" Don't Fragment:"); + s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); + s.append(" More Fragments:"); + s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); + return s; + } + case FieldBitSize: + return 3; + default: + break; + } + break; + case ip4_fragOfs: + switch(attrib) + { + case FieldName: + return QString("Fragment Offset"); + case FieldValue: + return data.frag_ofs(); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) (data.frag_ofs()), + (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg(data.frag_ofs()*8); + case FieldBitSize: + return 13; + default: + break; + } + break; + case ip4_ttl: + switch(attrib) + { + case FieldName: + return QString("Time to Live"); + case FieldValue: + return data.ttl(); + case FieldFrameValue: + return QByteArray(1, (char)data.ttl()); + case FieldTextValue: + return QString("%1").arg(data.ttl()); + default: + break; + } + break; + case ip4_proto: + { + switch(attrib) + { + case FieldName: + return QString("Protocol"); + case FieldValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return id; + } + case FieldFrameValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QByteArray(1, (char) id); + } + case FieldTextValue: + { + unsigned char id = data.is_override_proto() ? + data.proto() : payloadProtocolId(ProtocolIdIp); + return QString("0x%1"). + arg(id, 2, BASE_HEX, QChar('0')); + } + default: + break; + } + break; + } + case ip4_cksum: + { + switch(attrib) + { + case FieldName: + return QString("Header Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return cksum; + } + case FieldFrameValue: + { + QByteArray fv; + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + + fv.resize(2); + qToBigEndian((quint16) cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case ip4_srcAddr: + { + int u; + quint32 subnet, host, srcIp = 0; + + switch(data.src_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + srcIp = data.src_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) + u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.src_ip_count(); + subnet = data.src_ip() & data.src_ip_mask(); + host = (((data.src_ip() & ~data.src_ip_mask()) - u) & + ~data.src_ip_mask()); + srcIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.src_ip() & data.src_ip_mask(); + host = (qrand() & ~data.src_ip_mask()); + srcIp = subnet | host; + break; + default: + qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian(srcIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(srcIp).toString(); + default: + break; + } + break; + } + case ip4_dstAddr: + { + int u; + quint32 subnet, host, dstIp = 0; + + switch(data.dst_ip_mode()) + { + case OstProto::Ip4::e_im_fixed: + dstIp = data.dst_ip(); + break; + case OstProto::Ip4::e_im_inc_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_dec_host: + u = streamIndex % data.dst_ip_count(); + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & + ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + case OstProto::Ip4::e_im_random_host: + subnet = data.dst_ip() & data.dst_ip_mask(); + host = (qrand() & ~data.dst_ip_mask()); + dstIp = subnet | host; + break; + default: + qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + return dstIp; + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) dstIp, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QHostAddress(dstIp).toString(); + default: + break; + } + break; + } + + // Meta fields + case ip4_isOverrideVer: + switch(attrib) + { + case FieldValue: return data.is_override_ver(); + default: break; + } + break; + case ip4_isOverrideHdrLen: + switch(attrib) + { + case FieldValue: return data.is_override_hdrlen(); + default: break; + } + break; + case ip4_isOverrideTotLen: + switch(attrib) + { + case FieldValue: return data.is_override_totlen(); + default: break; + } + break; + case ip4_isOverrideProto: + switch(attrib) + { + case FieldValue: return data.is_override_proto(); + default: break; + } + break; + case ip4_isOverrideCksum: + switch(attrib) + { + case FieldValue: return data.is_override_cksum(); + default: break; + } + break; + + case ip4_srcAddrMode: + switch(attrib) + { + case FieldValue: return data.src_ip_mode(); + default: break; + } + break; + case ip4_srcAddrCount: + switch(attrib) + { + case FieldValue: return data.src_ip_count(); + default: break; + } + break; + case ip4_srcAddrMask: + switch(attrib) + { + case FieldValue: return data.src_ip_mask(); + default: break; + } + break; + + case ip4_dstAddrMode: + switch(attrib) + { + case FieldValue: return data.dst_ip_mode(); + default: break; + } + break; + case ip4_dstAddrCount: + switch(attrib) + { + case FieldValue: return data.dst_ip_count(); + default: break; + } + break; + case ip4_dstAddrMask: + switch(attrib) + { + case FieldValue: return data.dst_ip_mask(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool Ip4Protocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case ip4_ver: + { + uint version = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + ((version & 0xF) << 4) + | (data.ver_hdrlen() & 0x0F)); + break; + } + case ip4_hdrLen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_ver_hdrlen( + (data.ver_hdrlen() & 0xF0) + | (hdrLen & 0x0F)); + break; + } + case ip4_tos: + { + uint tos = value.toUInt(&isOk); + if (isOk) + data.set_tos(tos); + break; + } + case ip4_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case ip4_id: + { + uint id = value.toUInt(&isOk); + if (isOk) + data.set_id(id); + break; + } + case ip4_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case ip4_fragOfs: + { + uint fragOfs = value.toUInt(&isOk); + if (isOk) + data.set_frag_ofs(fragOfs); + break; + } + case ip4_ttl: + { + uint ttl = value.toUInt(&isOk); + if (isOk) + data.set_ttl(ttl); + break; + } + case ip4_proto: + { + uint proto = value.toUInt(&isOk); + if (isOk) + data.set_proto(proto); + break; + } + case ip4_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case ip4_srcAddr: + { + quint32 srcIp = value.toUInt(&isOk); + if (isOk) + data.set_src_ip(srcIp); + break; + } + case ip4_dstAddr: + { + quint32 dstIp = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip(dstIp); + break; + } + + // Meta-fields + case ip4_isOverrideVer: + { + bool ovr = value.toBool(); + data.set_is_override_ver(ovr); + isOk = true; + break; + } + case ip4_isOverrideHdrLen: + { + bool ovr = value.toBool(); + data.set_is_override_hdrlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideTotLen: + { + bool ovr = value.toBool(); + data.set_is_override_totlen(ovr); + isOk = true; + break; + } + case ip4_isOverrideProto: + { + bool ovr = value.toBool(); + data.set_is_override_proto(ovr); + isOk = true; + break; + } + case ip4_isOverrideCksum: + { + bool ovr = value.toBool(); + data.set_is_override_cksum(ovr); + isOk = true; + break; + } + + case ip4_srcAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_src_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_srcAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_count(count); + break; + } + case ip4_srcAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_src_ip_mask(mask); + break; + } + + case ip4_dstAddrMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.IpAddrMode_IsValid(mode)) + data.set_dst_ip_mode(OstProto::Ip4::IpAddrMode(mode)); + else + isOk = false; + break; + } + case ip4_dstAddrCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_count(count); + break; + } + case ip4_dstAddrMask: + { + quint32 mask = value.toUInt(&isOk); + if (isOk) + data.set_dst_ip_mask(mask); + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool Ip4Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + || (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed)) + return true; + else + return false; +} + +int Ip4Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.src_ip_count()); + + if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) + count = AbstractProtocol::lcm(count, data.dst_ip_count()); + + return count; +} + +quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + switch (cksumType) + { + case CksumIpPseudo: + { + quint32 sum; + + sum = fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_srcAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() >> 16; + sum += fieldData(ip4_dstAddr, FieldValue, streamIndex).toUInt() & 0xFFFF; + + sum += fieldData(ip4_proto, FieldValue, streamIndex).toUInt() & 0x00FF; + sum += (fieldData(ip4_totLen, FieldValue, streamIndex).toUInt() & 0xFFFF) - 20; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // Above calculation done assuming 'big endian' + // - so convert to host order + //return qFromBigEndian(sum); + return ~sum; + } + default: + break; + } + + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} diff --git a/common/ip4.h b/common/ip4.h new file mode 100644 index 0000000..1ea8128 --- /dev/null +++ b/common/ip4.h @@ -0,0 +1,99 @@ +/* +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 +*/ + +#ifndef _IPV4_H +#define _IPV4_H + +#include "abstractprotocol.h" +#include "ip4.pb.h" + +#define IP_FLAG_MF 0x1 +#define IP_FLAG_DF 0x2 +#define IP_FLAG_UNUSED 0x4 + +class Ip4Protocol : public AbstractProtocol +{ +public: + enum ip4field + { + ip4_ver = 0, + ip4_hdrLen, + ip4_tos, + ip4_totLen, + ip4_id, + ip4_flags, + ip4_fragOfs, + ip4_ttl, + ip4_proto, + ip4_cksum, + ip4_srcAddr, + ip4_dstAddr, + + // Meta-fields + ip4_isOverrideVer, + ip4_isOverrideHdrLen, + ip4_isOverrideTotLen, + ip4_isOverrideProto, + ip4_isOverrideCksum, + + ip4_srcAddrMode, + ip4_srcAddrCount, + ip4_srcAddrMask, + + ip4_dstAddrMode, + ip4_dstAddrCount, + ip4_dstAddrMask, + + ip4_fieldCount + }; + + Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip4Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + +private: + OstProto::Ip4 data; +}; + + +#endif diff --git a/common/ip4.proto b/common/ip4.proto new file mode 100644 index 0000000..be7391d --- /dev/null +++ b/common/ip4.proto @@ -0,0 +1,66 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// IPv4 +message Ip4 { + + enum IpAddrMode { + e_im_fixed = 0; + e_im_inc_host = 1; + e_im_dec_host = 2; + e_im_random_host = 3; + } + + optional bool is_override_ver = 1; + optional bool is_override_hdrlen = 2; + optional bool is_override_totlen = 3; + optional bool is_override_proto = 30; + optional bool is_override_cksum = 4; + + optional uint32 ver_hdrlen = 5 [default = 0x45]; + optional uint32 tos = 6; + optional uint32 totlen = 7; + optional uint32 id = 8 [default = 1234]; + optional uint32 flags = 9; + optional uint32 frag_ofs = 10; + optional uint32 ttl = 11 [default = 127]; + optional uint32 proto = 12; + optional uint32 cksum = 13; + + // Source IP + optional fixed32 src_ip = 14; + optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; + optional uint32 src_ip_count = 16 [default = 16]; + optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; + + // Destination IP + optional fixed32 dst_ip = 18; + optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; + optional uint32 dst_ip_count = 20 [default = 16]; + optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; + + //! \todo (LOW) IPv4 Options +} + +extend Protocol { + optional Ip4 ip4 = 301; +} diff --git a/common/ip4.ui b/common/ip4.ui new file mode 100644 index 0000000..3e98d7c --- /dev/null +++ b/common/ip4.ui @@ -0,0 +1,516 @@ + + ip4 + + + + 0 + 0 + 507 + 308 + + + + Form + + + + + + + + Override Version + + + + + + + false + + + + + + + + + + Override Header +Length (x4) + + + + + + + false + + + + + + + + + + TOS/DSCP + + + + + + + >HH; + + + + + + + + + + Override Length + + + + + + + false + + + + + + + Identification + + + + + + + >HH HH; + + + + + + + + + + + Fragment Offset (x8) + + + + + + + + + + Don't Fragment + + + + + + + More Fragments + + + + + + + Time To Live (TTL) + + + + + + + + + + + + + + false + + + >HH; + + + + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Protocol + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 101 + 20 + + + + + + + + Mode + + + + + + + Count + + + + + + + Mask + + + + + + + Source + + + + + + + 009.009.009.009; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + Destination + + + + + + + 000.000.000.000; + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + + + + + + + false + + + 009.009.009.009; + + + ... + + + + + + + + + + + + Options + + + + + + + false + + + TODO + + + + + + + false + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + cbIpVersionOverride + leIpVersion + cbIpHdrLenOverride + leIpHdrLen + leIpTos + cbIpLengthOverride + leIpLength + leIpId + leIpFragOfs + cbIpFlagsDf + cbIpFlagsMf + leIpTtl + cbIpProtocolOverride + leIpProto + cbIpCksumOverride + leIpCksum + leIpSrcAddr + cmbIpSrcAddrMode + leIpSrcAddrCount + leIpSrcAddrMask + leIpDstAddr + cmbIpDstAddrMode + leIpDstAddrCount + leIpDstAddrMask + leIpOptions + tbIpOptionsEdit + + + + + cbIpVersionOverride + toggled(bool) + leIpVersion + setEnabled(bool) + + + 108 + 11 + + + 195 + 11 + + + + + cbIpHdrLenOverride + toggled(bool) + leIpHdrLen + setEnabled(bool) + + + 113 + 67 + + + 166 + 43 + + + + + cbIpLengthOverride + toggled(bool) + leIpLength + setEnabled(bool) + + + 89 + 118 + + + 236 + 119 + + + + + cbIpCksumOverride + toggled(bool) + leIpCksum + setEnabled(bool) + + + 387 + 140 + + + 406 + 122 + + + + + cbIpProtocolOverride + toggled(bool) + leIpProto + setEnabled(bool) + + + 363 + 95 + + + 398 + 94 + + + + + diff --git a/common/ip4config.cpp b/common/ip4config.cpp new file mode 100644 index 0000000..261da7a --- /dev/null +++ b/common/ip4config.cpp @@ -0,0 +1,297 @@ +/* +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 +*/ + +#include "ip4config.h" +#include "ip4.h" + +#include + +Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + leIpVersion->setValidator(new QIntValidator(0, 15, this)); + + connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); + connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); +} + +Ip4ConfigForm::~Ip4ConfigForm() +{ +} + + +Ip4ConfigForm* Ip4ConfigForm::createInstance() +{ + return new Ip4ConfigForm; +} + +void Ip4ConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbIpVersionOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideVer, + AbstractProtocol::FieldValue + ).toBool()); + leIpVersion->setText( + proto->fieldData( + Ip4Protocol::ip4_ver, + AbstractProtocol::FieldValue + ).toString()); + + cbIpHdrLenOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpHdrLen->setText( + proto->fieldData( + Ip4Protocol::ip4_hdrLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpTos->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_tos, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpLengthOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + leIpLength->setText( + proto->fieldData( + Ip4Protocol::ip4_totLen, + AbstractProtocol::FieldValue + ).toString()); + + leIpId->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_id, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + leIpFragOfs->setText( + proto->fieldData( + Ip4Protocol::ip4_fragOfs, + AbstractProtocol::FieldValue + ).toString()); + cbIpFlagsDf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_DF) > 0); + cbIpFlagsMf->setChecked(( + proto->fieldData( + Ip4Protocol::ip4_flags, + AbstractProtocol::FieldValue + ).toUInt() & IP_FLAG_MF) > 0); + + leIpTtl->setText( + proto->fieldData( + Ip4Protocol::ip4_ttl, + AbstractProtocol::FieldValue + ).toString()); + + cbIpProtocolOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideProto, + AbstractProtocol::FieldValue + ).toBool()); + leIpProto->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_proto, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbIpCksumOverride->setChecked( + proto->fieldData( + Ip4Protocol::ip4_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); + leIpCksum->setText(uintToHexStr( + proto->fieldData( + Ip4Protocol::ip4_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + leIpSrcAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpSrcAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpSrcAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpSrcAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_srcAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + + leIpDstAddr->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddr, + AbstractProtocol::FieldValue + ).toUInt()).toString()); + cmbIpDstAddrMode->setCurrentIndex( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + leIpDstAddrCount->setText( + proto->fieldData( + Ip4Protocol::ip4_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + leIpDstAddrMask->setText(QHostAddress( + proto->fieldData( + Ip4Protocol::ip4_dstAddrMask, + AbstractProtocol::FieldValue + ).toUInt()).toString()); +} + +void Ip4ConfigForm::storeWidget(AbstractProtocol *proto) +{ + uint ff = 0; + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideVer, + cbIpVersionOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_ver, + leIpVersion->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideHdrLen, + cbIpHdrLenOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_hdrLen, + leIpHdrLen->text()); + + proto->setFieldData( + Ip4Protocol::ip4_tos, + hexStrToUInt(leIpTos->text())); + + proto->setFieldData( + Ip4Protocol::ip4_totLen, + leIpLength->text()); + proto->setFieldData( + Ip4Protocol::ip4_isOverrideTotLen, + cbIpLengthOverride->isChecked()); + + proto->setFieldData( + Ip4Protocol::ip4_id, + hexStrToUInt(leIpId->text())); + proto->setFieldData( + Ip4Protocol::ip4_fragOfs, + leIpFragOfs->text()); + + if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; + if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; + proto->setFieldData( + Ip4Protocol::ip4_flags, + ff); + + proto->setFieldData( + Ip4Protocol::ip4_ttl, + leIpTtl->text()); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideProto, + cbIpProtocolOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_proto, + hexStrToUInt(leIpProto->text())); + + proto->setFieldData( + Ip4Protocol::ip4_isOverrideCksum, + cbIpCksumOverride->isChecked()); + proto->setFieldData( + Ip4Protocol::ip4_cksum, + hexStrToUInt(leIpCksum->text())); + + proto->setFieldData( + Ip4Protocol::ip4_srcAddr, + QHostAddress(leIpSrcAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpSrcAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrCount, + leIpSrcAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_srcAddrMask, + QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); + + proto->setFieldData( + Ip4Protocol::ip4_dstAddr, + QHostAddress(leIpDstAddr->text()).toIPv4Address()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMode, + (OstProto::Ip4_IpAddrMode)cmbIpDstAddrMode->currentIndex()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrCount, + leIpDstAddrCount->text()); + proto->setFieldData( + Ip4Protocol::ip4_dstAddrMask, + QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); +} + +/* + * Slots + */ +void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpSrcAddrCount->setDisabled(true); + leIpSrcAddrMask->setDisabled(true); + } + else + { + leIpSrcAddrCount->setEnabled(true); + leIpSrcAddrMask->setEnabled(true); + } +} + +void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) +{ + if (index == OstProto::Ip4::e_im_fixed) + { + leIpDstAddrCount->setDisabled(true); + leIpDstAddrMask->setDisabled(true); + } + else + { + leIpDstAddrCount->setEnabled(true); + leIpDstAddrMask->setEnabled(true); + } +} diff --git a/common/ip4config.h b/common/ip4config.h new file mode 100644 index 0000000..6db7b97 --- /dev/null +++ b/common/ip4config.h @@ -0,0 +1,44 @@ +/* +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 +*/ + +#ifndef _IPV4_CONFIG_H +#define _IPV4_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip4.h" + +class Ip4ConfigForm : + public AbstractProtocolConfigForm, + private Ui::ip4 +{ + Q_OBJECT +public: + Ip4ConfigForm(QWidget *parent = 0); + virtual ~Ip4ConfigForm(); + + static Ip4ConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbIpSrcAddrMode_currentIndexChanged(int index); + void on_cmbIpDstAddrMode_currentIndexChanged(int index); +}; +#endif diff --git a/common/ip4over4.h b/common/ip4over4.h new file mode 100644 index 0000000..9ca1be7 --- /dev/null +++ b/common/ip4over4.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_H +#define _IP_4_OVER_4_H + +#include "ip4over4.pb.h" + +#include "comboprotocol.h" +#include "ip4.h" + +typedef ComboProtocol Ip4over4Combo; + +class Ip4over4Protocol : public Ip4over4Combo +{ +public: + Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip4over4Combo(stream, parent) + { + } + + static Ip4over4Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip4over4Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip4over4) + ->MutableExtension(OstProto::ip4_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip4over4)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip4)->CopyFrom( + protocol.GetExtension(OstProto::ip4over4).GetExtension( + OstProto::ip4_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip4over4.proto b/common/ip4over4.proto new file mode 100644 index 0000000..5a146c3 --- /dev/null +++ b/common/ip4over4.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip4.proto"; + +package OstProto; + +// IP 4over4 (also called IPIP) +message Ip4over4 { + extensions 1 to 2; +} + +extend Ip4over4 { + optional Ip4 ip4_outer = 1; + optional Ip4 ip4_inner = 2; +} + +extend Protocol { + optional Ip4over4 ip4over4 = 305; +} diff --git a/common/ip4over4config.h b/common/ip4over4config.h new file mode 100644 index 0000000..328b14e --- /dev/null +++ b/common/ip4over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_4_CONFIG_H +#define _IP_4_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip4.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over4FieldNumber, + Ip4ConfigForm, Ip4ConfigForm, + Ip4Protocol, Ip4Protocol + > Ip4over4ConfigForm; + +#endif diff --git a/common/ip4over6.h b/common/ip4over6.h new file mode 100644 index 0000000..41bcce0 --- /dev/null +++ b/common/ip4over6.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_H +#define _IP_4_OVER_6_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip4over6Protocol; + +#endif diff --git a/common/ip4over6.proto b/common/ip4over6.proto new file mode 100644 index 0000000..0482045 --- /dev/null +++ b/common/ip4over6.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 4over6 +message Ip4over6 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip4over6 ip4over6 = 304; +} diff --git a/common/ip4over6config.h b/common/ip4over6config.h new file mode 100644 index 0000000..b0ccf63 --- /dev/null +++ b/common/ip4over6config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_4_OVER_6_CONFIG_H +#define _IP_4_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp4over6FieldNumber, + Ip6ConfigForm, Ip4ConfigForm, + Ip6Protocol, Ip4Protocol + > Ip4over6ConfigForm; + +#endif diff --git a/common/ip4pdml.cpp b/common/ip4pdml.cpp new file mode 100644 index 0000000..f355260 --- /dev/null +++ b/common/ip4pdml.cpp @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 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 "ip4pdml.h" + +#include "hexdump.pb.h" +#include "ip4.pb.h" + +PdmlIp4Protocol::PdmlIp4Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; + + fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber); + fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); + fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); + fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); + //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); + fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); + fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); + fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); + fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); + fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); + fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); +} + +PdmlProtocol* PdmlIp4Protocol::createInstance() +{ + return new PdmlIp4Protocol(); +} + +void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if ((name == "ip.options") || + attributes.value("show").toString().startsWith("Options")) + { + options_ = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + } + else if (name == "ip.flags") + { + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); + } +} + +void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); + + ip4->set_is_override_ver(true); + ip4->set_is_override_hdrlen(true); + ip4->set_is_override_totlen(true); + ip4->set_is_override_proto(true); + ip4->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/ip4pdml.h b/common/ip4pdml.h new file mode 100644 index 0000000..64f818d --- /dev/null +++ b/common/ip4pdml.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP4_PDML_H +#define _IP4_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp4Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp4Protocol(); +private: + QByteArray options_; +}; + +#endif diff --git a/common/ip6.cpp b/common/ip6.cpp new file mode 100644 index 0000000..8833516 --- /dev/null +++ b/common/ip6.cpp @@ -0,0 +1,813 @@ +/* +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 +*/ + +#include "ip6.h" +#include + + +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, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 srcHi = 0, srcLo = 0; + + switch(data.src_addr_mode()) + { + case OstProto::Ip6::kFixed: + srcHi = data.src_addr_hi(); + srcLo = data.src_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.src_addr_count(); + if (data.src_addr_prefix() > 64) { + p = 64; + q = data.src_addr_prefix() - 64; + } else { + p = data.src_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.src_addr_hi() & maskHi; + prefixLo = data.src_addr_lo() & maskLo; + if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + srcHi = prefixHi | hostHi; + srcLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled src_addr_mode = %d", + data.src_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(srcHi, (uchar*) fv.data()); + qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).toString(); + else + return fv; + } + default: + break; + } + break; + } + + case ip6_dstAddress: + { + int u, p, q; + quint64 maskHi = 0, maskLo = 0; + quint64 prefixHi, prefixLo; + quint64 hostHi = 0, hostLo = 0; + quint64 dstHi = 0, dstLo = 0; + + switch(data.dst_addr_mode()) + { + case OstProto::Ip6::kFixed: + dstHi = data.dst_addr_hi(); + dstLo = data.dst_addr_lo(); + break; + case OstProto::Ip6::kIncHost: + case OstProto::Ip6::kDecHost: + case OstProto::Ip6::kRandomHost: + u = streamIndex % data.dst_addr_count(); + if (data.dst_addr_prefix() > 64) { + p = 64; + q = data.dst_addr_prefix() - 64; + } else { + p = data.dst_addr_prefix(); + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = data.dst_addr_hi() & maskHi; + prefixLo = data.dst_addr_lo() & maskLo; + if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; + } + else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { + hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; + hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; + } + else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + dstHi = prefixHi | hostHi; + dstLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled dst_addr_mode = %d", + data.dst_addr_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Destination"); + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(dstHi, (uchar*) fv.data()); + qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldTextValue) + return QHostAddress((quint8*)fv.constData()).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: + { + 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); + break; + } + case ip6_dstAddress: + { + 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); + 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; +} + +bool Ip6Protocol::isProtocolFrameValueVariable() const +{ + if ((data.src_addr_mode() != OstProto::Ip6::kFixed) + || (data.dst_addr_mode() != OstProto::Ip6::kFixed)) + return true; + else + return false; +} + +int Ip6Protocol::protocolFrameVariableCount() const +{ + int count = 1; + + 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) const +{ + if (cksumType == CksumIpPseudo) + { + QByteArray addr; + quint32 sum = 0; + + addr = fieldData(ip6_srcAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + addr = fieldData(ip6_dstAddress, FieldFrameValue, streamIndex) + .toByteArray(); + Q_ASSERT(addr.size() == 16); + for (int i = 0; i < addr.size(); i+=2) + sum += (quint8(addr.at(i)) << 8) + quint8(addr.at(i+1)); + + sum += fieldData(ip6_payloadLength, FieldValue, streamIndex) + .toUInt() & 0xFFFF; + sum += fieldData(ip6_nextHeader, FieldValue, streamIndex) + .toUInt() & 0xFF; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; + } + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + diff --git a/common/ip6.h b/common/ip6.h new file mode 100644 index 0000000..63f6e7e --- /dev/null +++ b/common/ip6.h @@ -0,0 +1,112 @@ +/* +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 +*/ + +#ifndef _IP6_H +#define _IP6_H + +#include "abstractprotocol.h" +#include "ip6.pb.h" + +/* +IPv6 Protocol Frame Format - + +-----+----------+-----------------------+ + | Ver | TrfClass | FlowLabel | + | (4) | (8) | (20) | + +-----+-------------+---------+----------+ + | Payload Length | NextHdr | HopLimit | + | (16) | (8) | (8) | + +-------------------+---------+----------+ + | | + | Source Address | + | (128) | + | | + +-----+------+------+------+------+------+ + | | + | Destination Address | + | (128) | + | | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class Ip6Protocol : public AbstractProtocol +{ +public: + enum ip6field + { + // Frame Fields + ip6_version = 0, + ip6_trafficClass, + ip6_flowLabel, + ip6_payloadLength, + ip6_nextHeader, + ip6_hopLimit, + ip6_srcAddress, + ip6_dstAddress, + + // Meta Fields + ip6_isOverrideVersion, + ip6_isOverridePayloadLength, + ip6_isOverrideNextHeader, + + ip6_srcAddrMode, + ip6_srcAddrCount, + ip6_srcAddrPrefix, + + ip6_dstAddrMode, + ip6_dstAddrCount, + ip6_dstAddrPrefix, + + ip6_fieldCount + }; + + Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~Ip6Protocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; +private: + OstProto::Ip6 data; +}; + +#endif diff --git a/common/ip6.proto b/common/ip6.proto new file mode 100644 index 0000000..d4831ed --- /dev/null +++ b/common/ip6.proto @@ -0,0 +1,61 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ip6 Protocol +message Ip6 { + + enum AddrMode { + kFixed = 0; + kIncHost = 1; + kDecHost = 2; + kRandomHost = 3; + } + + optional bool is_override_version = 1; + optional bool is_override_payload_length = 2; + optional bool is_override_next_header = 3; + + optional uint32 version = 4 [default = 0x6]; + optional uint32 traffic_class = 5; + optional uint32 flow_label = 6; + + optional uint32 payload_length = 7; + optional uint32 next_header = 8; + optional uint32 hop_limit = 9 [default = 127]; + + optional uint64 src_addr_hi = 10; + optional uint64 src_addr_lo = 11; + optional AddrMode src_addr_mode = 12 [default = kFixed]; + optional uint32 src_addr_count = 13 [default = 16]; + optional uint32 src_addr_prefix = 14 [default = 64]; + + optional uint64 dst_addr_hi = 15; + optional uint64 dst_addr_lo = 16; + optional AddrMode dst_addr_mode = 17 [default = kFixed]; + optional uint32 dst_addr_count = 18 [default = 16]; + optional uint32 dst_addr_prefix = 19 [default = 64]; +} + +extend Protocol { + optional Ip6 ip6 = 302; +} diff --git a/common/ip6.ui b/common/ip6.ui new file mode 100644 index 0000000..b9c10f2 --- /dev/null +++ b/common/ip6.ui @@ -0,0 +1,467 @@ + + Ip6 + + + + 0 + 0 + 506 + 233 + + + + Form + + + + + + + + Version + + + + + + + false + + + + + + + + + + + + + Qt::Vertical + + + + + + + Payload Length + + + + + + + false + + + + + + + Traffic Class + + + trafficClass + + + + + + + >HH; + + + + + + + + + + Next Header + + + + + + + false + + + HH; + + + + + + + + + + Flow Label + + + flowLabel + + + + + + + >H HH HH; + + + + + + + Hop Limit + + + hopLimit + + + + + + + + + + + + + + + + + + + false + + + + + + Qt::Horizontal + + + + 51 + 20 + + + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Prefix + + + + + + + Source + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + Destination + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Fixed + + + + + Increment Host + + + + + Decrement Host + + + + + Random Host + + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + + + + 10 + + + + + + + false + + + + 0 + 0 + + + + + 50 + 16777215 + + + + /009; + + + /64 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + isVersionOverride + version + trafficClass + flowLabel + isPayloadLengthOverride + payloadLength + isNextHeaderOverride + nextHeader + hopLimit + srcAddr + srcAddrModeCombo + srcAddrCount + srcAddrPrefix + dstAddr + dstAddrModeCombo + dstAddrCount + dstAddrPrefix + + + + + isVersionOverride + toggled(bool) + version + setEnabled(bool) + + + 67 + 22 + + + 195 + 11 + + + + + isPayloadLengthOverride + toggled(bool) + payloadLength + setEnabled(bool) + + + 319 + 28 + + + 493 + 29 + + + + + isNextHeaderOverride + toggled(bool) + nextHeader + setEnabled(bool) + + + 316 + 41 + + + 348 + 46 + + + + + diff --git a/common/ip6config.cpp b/common/ip6config.cpp new file mode 100644 index 0000000..1ddd20b --- /dev/null +++ b/common/ip6config.cpp @@ -0,0 +1,233 @@ +/* +Copyright (C) 2010-2014 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 "ip6config.h" +#include "ip6.h" +#include "ipv6addressvalidator.h" +#include + +Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + version->setValidator(new QIntValidator(0, 0xF, this)); + payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); + hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); + + srcAddr->setValidator(new IPv6AddressValidator(this)); + srcAddrCount->setValidator(new QIntValidator(this)); + //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); + + dstAddr->setValidator(new IPv6AddressValidator(this)); + dstAddrCount->setValidator(new QIntValidator(this)); + //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); +} + +AbstractProtocolConfigForm* Ip6ConfigForm::createInstance() +{ + return new Ip6ConfigForm; +} + +void Ip6ConfigForm::on_srcAddr_editingFinished() +{ + srcAddr->setText(QHostAddress(srcAddr->text()).toString()); +} + +void Ip6ConfigForm::on_dstAddr_editingFinished() +{ + dstAddr->setText(QHostAddress(dstAddr->text()).toString()); +} + +void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + srcAddrCount->setEnabled(enabled); + srcAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) +{ + bool enabled = (index > 0); + + dstAddrCount->setEnabled(enabled); + dstAddrPrefix->setEnabled(enabled); +} + +void Ip6ConfigForm::loadWidget(AbstractProtocol *ip6Proto) +{ + isVersionOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideVersion, + AbstractProtocol::FieldValue + ).toBool()); + version->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_version, + AbstractProtocol::FieldValue + ).toString()); + + trafficClass->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_trafficClass, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + flowLabel->setText(QString("%1").arg( + ip6Proto->fieldData( + Ip6Protocol::ip6_flowLabel, + AbstractProtocol::FieldValue + ).toUInt(), 5, BASE_HEX, QChar('0'))); + + isPayloadLengthOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + AbstractProtocol::FieldValue + ).toBool()); + payloadLength->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isNextHeaderOverride->setChecked( + ip6Proto->fieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + AbstractProtocol::FieldValue + ).toBool()); + nextHeader->setText(uintToHexStr( + ip6Proto->fieldData( + Ip6Protocol::ip6_nextHeader, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + hopLimit->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_hopLimit, + AbstractProtocol::FieldValue + ).toString()); + + srcAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddress, + AbstractProtocol::FieldTextValue + ).toString()); + srcAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + srcAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrCount, + AbstractProtocol::FieldValue + ).toString()); + srcAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_srcAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); + + dstAddr->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddress, + AbstractProtocol::FieldTextValue + ).toString()); + dstAddrModeCombo->setCurrentIndex( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrMode, + AbstractProtocol::FieldValue + ).toUInt()); + dstAddrCount->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrCount, + AbstractProtocol::FieldValue + ).toString()); + dstAddrPrefix->setText( + ip6Proto->fieldData( + Ip6Protocol::ip6_dstAddrPrefix, + AbstractProtocol::FieldValue + ).toString()); +} + +void Ip6ConfigForm::storeWidget(AbstractProtocol *ip6Proto) +{ + bool isOk; + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideVersion, + isVersionOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_version, + version->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_trafficClass, + trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_flowLabel, + flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverridePayloadLength, + isPayloadLengthOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_payloadLength, + payloadLength->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_isOverrideNextHeader, + isNextHeaderOverride->isChecked()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_nextHeader, + nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_hopLimit, + hopLimit->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddress, + srcAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrMode, + srcAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrCount, + srcAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_srcAddrPrefix, + srcAddrPrefix->text()); + + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddress, + dstAddr->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrMode, + dstAddrModeCombo->currentIndex()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrCount, + dstAddrCount->text()); + ip6Proto->setFieldData( + Ip6Protocol::ip6_dstAddrPrefix, + dstAddrPrefix->text()); +} + diff --git a/common/ip6config.h b/common/ip6config.h new file mode 100644 index 0000000..0ea08c1 --- /dev/null +++ b/common/ip6config.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ +#ifndef _IP6_CONFIG_H +#define _IP6_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_ip6.h" + +class Ip6ConfigForm : + public AbstractProtocolConfigForm, + private Ui::Ip6 +{ + Q_OBJECT +public: + Ip6ConfigForm(QWidget *parent = 0); + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *ip6Proto); + virtual void storeWidget(AbstractProtocol *ip6Proto); + +private slots: + void on_srcAddr_editingFinished(); + void on_dstAddr_editingFinished(); + void on_srcAddrModeCombo_currentIndexChanged(int index); + void on_dstAddrModeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/ip6over4.h b/common/ip6over4.h new file mode 100644 index 0000000..08ee19b --- /dev/null +++ b/common/ip6over4.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_H +#define _IP_6_OVER_4_H + +#include "comboprotocol.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over4Protocol; + +#endif diff --git a/common/ip6over4.proto b/common/ip6over4.proto new file mode 100644 index 0000000..b8b0afd --- /dev/null +++ b/common/ip6over4.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// IP Tunelling - IP 6over4 +message Ip6over4 { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional Ip6over4 ip6over4 = 303; +} diff --git a/common/ip6over4config.h b/common/ip6over4config.h new file mode 100644 index 0000000..b3c2390 --- /dev/null +++ b/common/ip6over4config.h @@ -0,0 +1,35 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_4_CONFIG_H +#define _IP_6_OVER_4_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over4FieldNumber, + Ip4ConfigForm, Ip6ConfigForm, + Ip4Protocol, Ip6Protocol + > Ip6over4ConfigForm; + +#endif diff --git a/common/ip6over6.h b/common/ip6over6.h new file mode 100644 index 0000000..133d4f9 --- /dev/null +++ b/common/ip6over6.h @@ -0,0 +1,91 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_H +#define _IP_6_OVER_6_H + +#include "ip6over6.pb.h" + +#include "comboprotocol.h" +#include "ip6.h" + +typedef ComboProtocol Ip6over6Combo; + +class Ip6over6Protocol : public Ip6over6Combo +{ +public: + Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) + : Ip6over6Combo(stream, parent) + { + } + + static Ip6over6Protocol* createInstance(StreamBase *stream, + AbstractProtocol *parent) + { + return new Ip6over6Protocol(stream, parent); + } + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const + { + OstProto::Protocol tempProto; + + protoA->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_outer) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + tempProto.Clear(); + + protoB->protoDataCopyInto(tempProto); + protocol.MutableExtension(OstProto::ip6over6) + ->MutableExtension(OstProto::ip6_inner) + ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); + + protocol.mutable_protocol_id()->set_id(protocolNumber()); + } + + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) + { + if (protocol.protocol_id().id() == protocolNumber() + && protocol.HasExtension(OstProto::ip6over6)) + { + OstProto::Protocol tempProto; + + // NOTE: To use protoX->protoDataCopyFrom() we need to arrange + // so that it sees its own protocolNumber() and its own extension + // in 'protocol' + tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_outer)); + protoA->protoDataCopyFrom(tempProto); + + tempProto.Clear(); + + tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); + tempProto.MutableExtension(OstProto::ip6)->CopyFrom( + protocol.GetExtension(OstProto::ip6over6).GetExtension( + OstProto::ip6_inner)); + protoB->protoDataCopyFrom(tempProto); + } + } +}; + +#endif diff --git a/common/ip6over6.proto b/common/ip6over6.proto new file mode 100644 index 0000000..f65f6ea --- /dev/null +++ b/common/ip6over6.proto @@ -0,0 +1,37 @@ +/* +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 +*/ + +import "protocol.proto"; +import "ip6.proto"; + +package OstProto; + +// IP Tunnelling - IP 6over6 +message Ip6over6 { + extensions 1 to 2; +} + +extend Ip6over6 { + optional Ip6 ip6_outer = 1; + optional Ip6 ip6_inner = 2; +} + +extend Protocol { + optional Ip6over6 ip6over6 = 306; +} diff --git a/common/ip6over6config.h b/common/ip6over6config.h new file mode 100644 index 0000000..4324fe7 --- /dev/null +++ b/common/ip6over6config.h @@ -0,0 +1,33 @@ +/* +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 +*/ + +#ifndef _IP_6_OVER_6_CONFIG_H +#define _IP_6_OVER_6_CONFIG_H + +#include "comboprotocolconfig.h" +#include "ip6config.h" +#include "ip6.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kIp6over6FieldNumber, + Ip6ConfigForm, Ip6ConfigForm, + Ip6Protocol, Ip6Protocol + > Ip6over6ConfigForm; + +#endif diff --git a/common/ip6pdml.cpp b/common/ip6pdml.cpp new file mode 100644 index 0000000..2f3a7f8 --- /dev/null +++ b/common/ip6pdml.cpp @@ -0,0 +1,76 @@ +/* +Copyright (C) 2011 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 "ip6pdml.h" + +#include "ip6.pb.h" + +PdmlIp6Protocol::PdmlIp6Protocol() +{ + ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; + + fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); + fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); + fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); + fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); + fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); + fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); + + // ipv6.src and ipv6.dst handled as unknown fields +} + +PdmlProtocol* PdmlIp6Protocol::createInstance() +{ + return new PdmlIp6Protocol(); +} + +void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + + if (name == "ipv6.src") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "ipv6.dst") + { + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + QString addrHexStr = attributes.value("value").toString(); + + ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } +} + +void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); + + ip6->set_is_override_version(true); + ip6->set_is_override_payload_length(true); + ip6->set_is_override_next_header(true); +} + diff --git a/common/ip6pdml.h b/common/ip6pdml.h new file mode 100644 index 0000000..4f766b4 --- /dev/null +++ b/common/ip6pdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _IP6_PDML_H +#define _IP6_PDML_H + +#include "pdmlprotocol.h" + +class PdmlIp6Protocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlIp6Protocol(); +}; + +#endif diff --git a/common/iputils.h b/common/iputils.h new file mode 100644 index 0000000..0d6a067 --- /dev/null +++ b/common/iputils.h @@ -0,0 +1,122 @@ +/* +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 +*/ + +#ifndef _IP_UTILS_H +#define _IP_UTILS_H + +namespace ipUtils { +enum AddrMode { + kFixed = 0, + kIncrement = 1, + kDecrement = 2, + kRandom = 3 +}; + +quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, + int index) +{ + int u; + quint32 mask = ((1< 64) { + p = 64; + q = prefix - 64; + } else { + p = prefix; + q = 0; + } + if (p > 0) + maskHi = ~((quint64(1) << p) - 1); + if (q > 0) + maskLo = ~((quint64(1) << q) - 1); + prefixHi = baseIpHi & maskHi; + prefixLo = baseIpLo & maskLo; + if (mode == kIncrement) { + hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; + } + else if (mode == kDecrement) { + hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; + hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; + } + else if (mode==kRandom) { + hostHi = qrand() & ~maskHi; + hostLo = qrand() & ~maskLo; + } + ipHi = prefixHi | hostHi; + ipLo = prefixLo | hostLo; + break; + default: + qWarning("Unhandled mode = %d", mode); + } +} + +} // namespace ipUtils +#endif diff --git a/common/ipv4addressdelegate.h b/common/ipv4addressdelegate.h new file mode 100644 index 0000000..9e80d17 --- /dev/null +++ b/common/ipv4addressdelegate.h @@ -0,0 +1,58 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include +#include + +class IPv4AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv4AddressDelegate(QObject *parent = 0); + ~IPv4AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv4AddressDelegate::~IPv4AddressDelegate() +{ +} + +inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressdelegate.h b/common/ipv6addressdelegate.h new file mode 100644 index 0000000..9e3c30e --- /dev/null +++ b/common/ipv6addressdelegate.h @@ -0,0 +1,60 @@ +/* +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 +*/ +#ifndef _IPV4_ADDRESS_DELEGATE +#define _IPV4_ADDRESS_DELEGATE + +#include "ipv6addressvalidator.h" + +#include +#include + +class IPv6AddressDelegate : public QItemDelegate +{ + Q_OBJECT +public: + IPv6AddressDelegate(QObject *parent = 0); + ~IPv6AddressDelegate(); + + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +inline IPv6AddressDelegate::~IPv6AddressDelegate() +{ +} + +inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QLineEdit *ipEdit; + + ipEdit = static_cast(QItemDelegate::createEditor( + parent, option, index)); + + ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); + + return ipEdit; +} +#endif + diff --git a/common/ipv6addressvalidator.h b/common/ipv6addressvalidator.h new file mode 100644 index 0000000..ffbd7d5 --- /dev/null +++ b/common/ipv6addressvalidator.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _IPV6_ADDRESS_VALIDATOR_H +#define _IPV6_ADDRESS_VALIDATOR_H + +#include +#include + +class IPv6AddressValidator : public QValidator +{ +public: + IPv6AddressValidator(QObject *parent = 0) + : QValidator(parent) + { + _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); + } + ~IPv6AddressValidator() {} + + virtual QValidator::State validate(QString &input, int& /*pos*/) const + { + QValidator::State state; + QHostAddress addr(input); + + //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); + + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + state = Acceptable; + else + if (_ip6ValidChars.exactMatch(input)) + state = Intermediate; + else + state = Invalid; + //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, + //input.toAscii().constData(), pos); + return state; + } + virtual void fixup(QString &input) const + { + input.append("::"); + QHostAddress addr(input); + int len = input.size(); + + //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); + + while (addr.protocol() != QAbstractSocket::IPv6Protocol) + { + len--; + Q_ASSERT(len >= 0); + addr.setAddress(input.left(len)); + } + + input = addr.toString(); + } +private: + QRegExp _ip6ValidChars; +}; + +#endif diff --git a/common/llc.cpp b/common/llc.cpp new file mode 100644 index 0000000..67b370d --- /dev/null +++ b/common/llc.cpp @@ -0,0 +1,264 @@ +/* +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 +*/ + +#include "llc.h" + +LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +LlcProtocol::~LlcProtocol() +{ +} + +AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new LlcProtocol(stream, parent); +} + +quint32 LlcProtocol::protocolNumber() const +{ + return OstProto::Protocol::kLlcFieldNumber; +} + +void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::llc)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::llc)) + data.MergeFrom(protocol.GetExtension(OstProto::llc)); +} + +QString LlcProtocol::name() const +{ + return QString("802.3 Logical Link Control"); +} + +QString LlcProtocol::shortName() const +{ + return QString("LLC"); +} + +AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const +{ + return ProtocolIdLlc; +} + +int LlcProtocol::fieldCount() const +{ + return llc_fieldCount; +} + +AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case llc_dsap: + case llc_ssap: + case llc_ctl: + break; + + case llc_is_override_dsap: + case llc_is_override_ssap: + case llc_is_override_ctl: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + quint32 id; + quint8 dsap, ssap, ctl; + + id = payloadProtocolId(ProtocolIdLlc); + dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; + ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; + ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; + + switch (index) + { + case llc_dsap: + switch(attrib) + { + case FieldName: + return QString("DSAP"); + case FieldValue: + return dsap; + case FieldTextValue: + return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(dsap)); + default: + break; + } + break; + case llc_ssap: + switch(attrib) + { + case FieldName: + return QString("SSAP"); + case FieldValue: + return ssap; + case FieldTextValue: + return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ssap)); + default: + break; + } + break; + case llc_ctl: + switch(attrib) + { + case FieldName: + return QString("Control"); + case FieldValue: + return ctl; + case FieldTextValue: + return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + return QByteArray(1, (char)(ctl)); + default: + break; + } + break; + + + // Meta fields + case llc_is_override_dsap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dsap(); + default: + break; + } + break; + } + case llc_is_override_ssap: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ssap(); + default: + break; + } + break; + } + case llc_is_override_ctl: + { + switch(attrib) + { + case FieldValue: + return data.is_override_ctl(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool LlcProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case llc_dsap: + { + uint dsap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_dsap(dsap); + break; + } + case llc_ssap: + { + uint ssap = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ssap(ssap); + break; + } + case llc_ctl: + { + uint ctl = value.toUInt(&isOk) & 0xFF; + if (isOk) + data.set_ctl(ctl); + break; + } + case llc_is_override_dsap: + { + bool ovr = value.toBool(); + data.set_is_override_dsap(ovr); + isOk = true; + break; + } + case llc_is_override_ssap: + { + bool ovr = value.toBool(); + data.set_is_override_ssap(ovr); + isOk = true; + break; + } + case llc_is_override_ctl: + { + bool ovr = value.toBool(); + data.set_is_override_ctl(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} diff --git a/common/llc.h b/common/llc.h new file mode 100644 index 0000000..a723177 --- /dev/null +++ b/common/llc.h @@ -0,0 +1,71 @@ +/* +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 +*/ + +#ifndef _LLC_H +#define _LLC_H + +#include "abstractprotocol.h" + +#include "llc.pb.h" + +class LlcProtocol : public AbstractProtocol +{ +public: + enum llcfield + { + llc_dsap = 0, + llc_ssap, + llc_ctl, + + // Meta fields + llc_is_override_dsap, + llc_is_override_ssap, + llc_is_override_ctl, + + llc_fieldCount + }; + + LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~LlcProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Llc data; +}; + +#endif diff --git a/common/llc.proto b/common/llc.proto new file mode 100644 index 0000000..360b935 --- /dev/null +++ b/common/llc.proto @@ -0,0 +1,36 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Llc { + optional bool is_override_dsap = 4; + optional bool is_override_ssap = 5; + optional bool is_override_ctl = 6; + + optional uint32 dsap = 1; + optional uint32 ssap = 2; + optional uint32 ctl = 3; +} + +extend Protocol { + optional Llc llc = 202; +} diff --git a/common/llc.ui b/common/llc.ui new file mode 100644 index 0000000..e61f54e --- /dev/null +++ b/common/llc.ui @@ -0,0 +1,161 @@ + + llc + + + + 0 + 0 + 396 + 98 + + + + + 0 + 0 + + + + Form + + + + + + LLC + + + + + + DSAP + + + + + + + false + + + >HH; + + + + + + + SSAP + + + + + + + false + + + >HH; + + + + + + + Control + + + + + + + false + + + >HH; + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideDsap + toggled(bool) + leDsap + setEnabled(bool) + + + 54 + 34 + + + 92 + 33 + + + + + cbOverrideSsap + toggled(bool) + leSsap + setEnabled(bool) + + + 167 + 34 + + + 192 + 33 + + + + + cbOverrideControl + toggled(bool) + leControl + setEnabled(bool) + + + 285 + 34 + + + 310 + 33 + + + + + diff --git a/common/llcconfig.cpp b/common/llcconfig.cpp new file mode 100644 index 0000000..6ba785c --- /dev/null +++ b/common/llcconfig.cpp @@ -0,0 +1,100 @@ +/* +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 +*/ + +#include "llcconfig.h" +#include "llc.h" + +LlcConfigForm::LlcConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +LlcConfigForm::~LlcConfigForm() +{ +} + +LlcConfigForm* LlcConfigForm::createInstance() +{ + return new LlcConfigForm; +} + +void LlcConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideDsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_dsap, + AbstractProtocol::FieldValue + ).toBool()); + leDsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_dsap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideSsap->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ssap, + AbstractProtocol::FieldValue + ).toBool()); + leSsap->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ssap, + AbstractProtocol::FieldValue + ).toUInt(), 1)); + + cbOverrideControl->setChecked( + proto->fieldData( + LlcProtocol::llc_is_override_ctl, + AbstractProtocol::FieldValue + ).toBool()); + leControl->setText(uintToHexStr( + proto->fieldData( + LlcProtocol::llc_ctl, + AbstractProtocol::FieldValue + ).toUInt(), 1)); +} + +void +LlcConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + LlcProtocol::llc_is_override_dsap, + cbOverrideDsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_dsap, + leDsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ssap, + cbOverrideSsap->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ssap, + leSsap->text().toUInt(&isOk, BASE_HEX)); + + proto->setFieldData( + LlcProtocol::llc_is_override_ctl, + cbOverrideControl->isChecked()); + proto->setFieldData( + LlcProtocol::llc_ctl, + leControl->text().toUInt(&isOk, BASE_HEX)); +} + diff --git a/common/llcconfig.h b/common/llcconfig.h new file mode 100644 index 0000000..08bd3f4 --- /dev/null +++ b/common/llcconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _LLC_CONFIG_H +#define _LLC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_llc.h" + +class LlcConfigForm : + public AbstractProtocolConfigForm, + private Ui::llc +{ + Q_OBJECT +public: + LlcConfigForm(QWidget *parent = 0); + virtual ~LlcConfigForm(); + + static LlcConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/llcpdml.cpp b/common/llcpdml.cpp new file mode 100644 index 0000000..a5584cc --- /dev/null +++ b/common/llcpdml.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2011 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 "llcpdml.h" + +#include "llc.pb.h" +#include "snap.pb.h" + +#include + +PdmlLlcProtocol::PdmlLlcProtocol() +{ + ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; + + fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); + fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); + fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); +} + +PdmlProtocol* PdmlLlcProtocol::createInstance() +{ + return new PdmlLlcProtocol(); +} + +void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "llc.oui") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSnapFieldNumber); + + OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_oui(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_oui(true); + } + else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) + { + OstProto::Snap *snap = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::snap); + + bool isOk; + snap->set_type(attributes.value("value").toString() + .toUInt(&isOk, kBaseHex)); + snap->set_is_override_type(true); + } +} + +void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); + + llc->set_is_override_dsap(true); + llc->set_is_override_ssap(true); + llc->set_is_override_ctl(true); +} + diff --git a/common/llcpdml.h b/common/llcpdml.h new file mode 100644 index 0000000..ace0dcb --- /dev/null +++ b/common/llcpdml.h @@ -0,0 +1,39 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _LLC_PDML_H +#define _LLC_PDML_H + +#include "pdmlprotocol.h" + +class PdmlLlcProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlLlcProtocol(); +}; + +#endif diff --git a/common/mac.cpp b/common/mac.cpp new file mode 100644 index 0000000..70d047b --- /dev/null +++ b/common/mac.cpp @@ -0,0 +1,351 @@ +/* +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 +*/ + +#include "mac.h" + +#include + +#define uintToMacStr(num) \ + QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ + .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() + +MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +MacProtocol::~MacProtocol() +{ +} + +AbstractProtocol* MacProtocol::createInstance(StreamBase *stream + , AbstractProtocol *parent) +{ + return new MacProtocol(stream, parent); +} + +quint32 MacProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMacFieldNumber; +} + +void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mac)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mac)) + data.MergeFrom(protocol.GetExtension(OstProto::mac)); +} + +QString MacProtocol::name() const +{ + return QString("Media Access Protocol"); +} + +QString MacProtocol::shortName() const +{ + return QString("MAC"); +} + +int MacProtocol::fieldCount() const +{ + return mac_fieldCount; +} + +AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case mac_dstAddr: + case mac_srcAddr: + break; + + case mac_dstMacMode: + case mac_dstMacCount: + case mac_dstMacStep: + case mac_srcMacMode: + case mac_srcMacCount: + case mac_srcMacStep: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case mac_dstAddr: + { + int u; + quint64 dstMac = 0; + + switch (data.dst_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + dstMac = data.dst_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.dst_mac_count()) * + data.dst_mac_step(); + dstMac = data.dst_mac() - u; + break; + default: + qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Desination"); + case FieldValue: + return dstMac; + case FieldTextValue: + return uintToMacStr(dstMac); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(dstMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + case mac_srcAddr: + { + int u; + quint64 srcMac = 0; + + switch (data.src_mac_mode()) + { + case OstProto::Mac::e_mm_fixed: + srcMac = data.src_mac(); + break; + case OstProto::Mac::e_mm_inc: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() + u; + break; + case OstProto::Mac::e_mm_dec: + u = (streamIndex % data.src_mac_count()) * + data.src_mac_step(); + srcMac = data.src_mac() - u; + break; + default: + qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); + } + + switch(attrib) + { + case FieldName: + return QString("Source"); + case FieldValue: + return srcMac; + case FieldTextValue: + return uintToMacStr(srcMac); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(8); + qToBigEndian(srcMac, (uchar*) fv.data()); + fv.remove(0, 2); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case mac_dstMacMode: + switch(attrib) + { + case FieldValue: return data.dst_mac_mode(); + default: break; + } + break; + case mac_dstMacCount: + switch(attrib) + { + case FieldValue: return data.dst_mac_count(); + default: break; + } + break; + case mac_dstMacStep: + switch(attrib) + { + case FieldValue: return data.dst_mac_step(); + default: break; + } + break; + case mac_srcMacMode: + switch(attrib) + { + case FieldValue: return data.src_mac_mode(); + default: break; + } + break; + case mac_srcMacCount: + switch(attrib) + { + case FieldValue: return data.src_mac_count(); + default: break; + } + break; + case mac_srcMacStep: + switch(attrib) + { + case FieldValue: return data.src_mac_step(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool MacProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case mac_dstAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_dst_mac(mac); + break; + } + case mac_srcAddr: + { + quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); + if (isOk) + data.set_src_mac(mac); + break; + } + + // Meta-Fields + case mac_dstMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_dstMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_count(count); + break; + } + case mac_dstMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_dst_mac_step(step); + break; + } + case mac_srcMacMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.MacAddrMode_IsValid(mode)) + data.set_src_mac_mode((OstProto::Mac::MacAddrMode) mode); + else + isOk = false; + break; + } + case mac_srcMacCount: + { + uint count = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_count(count); + break; + } + case mac_srcMacStep: + { + uint step = value.toUInt(&isOk); + if (isOk) + data.set_src_mac_step(step); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool MacProtocol::isProtocolFrameValueVariable() const +{ + if ((data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) || + (data.src_mac_mode() != OstProto::Mac::e_mm_fixed)) + return true; + else + return false; +} + +int MacProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.dst_mac_count()); + + if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) + count = AbstractProtocol::lcm(count, data.src_mac_count()); + + return count; +} + diff --git a/common/mac.h b/common/mac.h new file mode 100644 index 0000000..85e0ad5 --- /dev/null +++ b/common/mac.h @@ -0,0 +1,73 @@ +/* +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 +*/ + +#ifndef _MAC_H +#define _MAC_H + +#include "abstractprotocol.h" + +#include "mac.pb.h" + +class MacProtocol : public AbstractProtocol +{ +public: + enum macfield + { + mac_dstAddr = 0, + mac_srcAddr, + + mac_dstMacMode, + mac_dstMacCount, + mac_dstMacStep, + mac_srcMacMode, + mac_srcMacCount, + mac_srcMacStep, + + mac_fieldCount + }; + + MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MacProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Mac data; +}; + +#endif diff --git a/common/mac.proto b/common/mac.proto new file mode 100644 index 0000000..2055223 --- /dev/null +++ b/common/mac.proto @@ -0,0 +1,48 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Ethernet +message Mac { + + enum MacAddrMode { + e_mm_fixed = 0; + e_mm_inc = 1; + e_mm_dec = 2; + } + + // Dst Mac + optional uint64 dst_mac = 1; + optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; + optional uint32 dst_mac_count = 3 [default = 16]; + optional uint32 dst_mac_step = 4 [default = 1]; + + // Src Mac + optional uint64 src_mac = 5; + optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; + optional uint32 src_mac_count = 7 [default = 16]; + optional uint32 src_mac_step = 8 [default = 1]; +} + +extend Protocol { + optional Mac mac = 100; +} diff --git a/common/mac.ui b/common/mac.ui new file mode 100644 index 0000000..821cf00 --- /dev/null +++ b/common/mac.ui @@ -0,0 +1,188 @@ + + mac + + + + 0 + 0 + 391 + 116 + + + + Form + + + + + + Address + + + + + + + Mode + + + + + + + Count + + + + + + + Step + + + + + + + Destination + + + + + + + + 120 + 0 + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + 0 + + + + + + + false + + + + + + 0 + + + + + + + Source + + + + + + + >HH HH HH HH HH HH; + + + + + + + + + + + Fixed + + + + + Increment + + + + + Decrement + + + + + + + + false + + + + + + + + + + false + + + + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/macconfig.cpp b/common/macconfig.cpp new file mode 100644 index 0000000..f17c140 --- /dev/null +++ b/common/macconfig.cpp @@ -0,0 +1,148 @@ +/* +Copyright (C) 2010,2013-2014 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 "macconfig.h" +#include "mac.h" + +#define MAX_MAC_ITER_COUNT 256 + +MacConfigForm::MacConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); + + setupUi(this); + leDstMac->setValidator(new QRegExpValidator(reMac, this)); + leSrcMac->setValidator(new QRegExpValidator(reMac, this)); + leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); + leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); +} + +MacConfigForm::~MacConfigForm() +{ +} + +MacConfigForm* MacConfigForm::createInstance() +{ + MacConfigForm *f = new MacConfigForm; + return f; +} + +void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leDstMacCount->setEnabled(false); + leDstMacStep->setEnabled(false); + } + else + { + leDstMacCount->setEnabled(true); + leDstMacStep->setEnabled(true); + } +} + +void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) +{ + if (index == OstProto::Mac::e_mm_fixed) + { + leSrcMacCount->setEnabled(false); + leSrcMacStep->setEnabled(false); + } + else + { + leSrcMacCount->setEnabled(true); + leSrcMacStep->setEnabled(true); + } +} + +void MacConfigForm::loadWidget(AbstractProtocol *proto) +{ + leDstMac->setText( + proto->fieldData( + MacProtocol::mac_dstAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbDstMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_dstMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leDstMacCount->setText( + proto->fieldData( + MacProtocol::mac_dstMacCount, + AbstractProtocol::FieldValue + ).toString()); + leDstMacStep->setText( + proto->fieldData( + MacProtocol::mac_dstMacStep, + AbstractProtocol::FieldValue + ).toString()); + + leSrcMac->setText( + proto->fieldData( + MacProtocol::mac_srcAddr, + AbstractProtocol::FieldTextValue + ).toString()); + cmbSrcMacMode->setCurrentIndex( + proto->fieldData( + MacProtocol::mac_srcMacMode, + AbstractProtocol::FieldValue + ).toUInt()); + leSrcMacCount->setText( + proto->fieldData( + MacProtocol::mac_srcMacCount, + AbstractProtocol::FieldValue + ).toString()); + leSrcMacStep->setText( + proto->fieldData( + MacProtocol::mac_srcMacStep, + AbstractProtocol::FieldValue + ).toString()); +} + +void MacConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + MacProtocol::mac_dstAddr, + leDstMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_dstMacMode, + cmbDstMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_dstMacCount, + leDstMacCount->text()); + proto->setFieldData( + MacProtocol::mac_dstMacStep, + leDstMacStep->text()); + + proto->setFieldData( + MacProtocol::mac_srcAddr, + leSrcMac->text().remove(QChar(' '))); + proto->setFieldData( + MacProtocol::mac_srcMacMode, + cmbSrcMacMode->currentIndex()); + proto->setFieldData( + MacProtocol::mac_srcMacCount, + leSrcMacCount->text()); + proto->setFieldData( + MacProtocol::mac_srcMacStep, + leSrcMacStep->text()); +} + diff --git a/common/macconfig.h b/common/macconfig.h new file mode 100644 index 0000000..952c64e --- /dev/null +++ b/common/macconfig.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _MAC_CONFIG_H +#define _MAC_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_mac.h" + +class MacConfigForm : + public AbstractProtocolConfigForm, + private Ui::mac +{ + Q_OBJECT +public: + MacConfigForm(QWidget *parent = 0); + virtual ~MacConfigForm(); + + static MacConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbDstMacMode_currentIndexChanged(int index); + void on_cmbSrcMacMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/mld.cpp b/common/mld.cpp new file mode 100644 index 0000000..52b48a5 --- /dev/null +++ b/common/mld.cpp @@ -0,0 +1,543 @@ +/* +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 +*/ + +#include "mld.h" + +#include "iputils.h" + +#include +#include + +MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) + : GmpProtocol(stream, parent) +{ + _hasPayload = false; + + data.set_type(kMldV1Query); +} + +MldProtocol::~MldProtocol() +{ +} + +AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new MldProtocol(stream, parent); +} + +quint32 MldProtocol::protocolNumber() const +{ + return OstProto::Protocol::kMldFieldNumber; +} + +void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::mld)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::mld)) + data.MergeFrom(protocol.GetExtension(OstProto::mld)); +} + +QString MldProtocol::name() const +{ + return QString("Multicast Listener Discovery"); +} + +QString MldProtocol::shortName() const +{ + return QString("MLD"); +} + +quint32 MldProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x3a; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = GmpProtocol::fieldFlags(index); + + switch(index) + { + case kMldMrt: + case kMldRsvd: + if (msgType() != kMldV2Report) + flags |= FrameField; + break; + default: + break; + } + + return flags; +} + +QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case kRsvdMrtCode: + { + switch(attrib) + { + case FieldName: return QString("Code"); + default: break; + } + break; + } + + case kMldMrt: + { + quint16 mrt = 0, mrcode = 0; + + if (msgType() == kMldV2Query) + { + mrt = data.max_response_time(); + mrcode = mrc(mrt); + } + else if (msgType() == kMldV1Query) + mrcode = mrt = data.max_response_time() & 0xFFFF; + + switch(attrib) + { + case FieldName: + if (isQuery()) + return QString("Max Response Time"); + return QString("Reserved"); + case FieldValue: + return mrt; + case FieldTextValue: + return QString("%1 ms").arg(mrt); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(mrcode, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kMldRsvd: + { + quint16 rsvd = 0; + + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return rsvd; + case FieldTextValue: + return QString("%1").arg(rsvd); + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(rsvd, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case kGroupAddress: + { + quint64 grpHi = 0, grpLo = 0; + + ipUtils::ipAddress( + data.group_address().v6_hi(), + data.group_address().v6_lo(), + data.group_prefix(), + ipUtils::AddrMode(data.group_mode()), + data.group_count(), + streamIndex, + grpHi, + grpLo); + + switch(attrib) + { + case FieldName: + return QString("Group Address"); + case FieldValue: + case FieldTextValue: + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16); + qToBigEndian(grpHi, (uchar*) fv.data()); + qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); + if (attrib == FieldFrameValue) + return fv; + else + return QHostAddress((quint8*)fv.constData()).toString(); + } + default: + break; + } + break; + } + case kSources: + { + switch(attrib) + { + case FieldName: + return QString("Source List"); + case FieldValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list; + } + case FieldFrameValue: + { + QByteArray fv; + fv.resize(16 * data.sources_size()); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)(fv.data() + i*16)); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)(fv.data() + i*16 + 8)); + } + return fv; + } + case FieldTextValue: + { + QStringList list; + QByteArray fv; + fv.resize(16); + for (int i = 0; i < data.sources_size(); i++) + { + qToBigEndian(data.sources(i).v6_hi(), + (uchar*)fv.data()); + qToBigEndian(data.sources(i).v6_lo(), + (uchar*)fv.data()+8); + + list << QHostAddress((quint8*)fv.constData()).toString(); + } + return list.join(", "); + } + default: + break; + } + break; + } + case kGroupRecords: + { + switch(attrib) + { + case FieldValue: + { + QVariantList grpRecords = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + QVariantMap grpRec = grpRecords.at(i).toMap(); + OstProto::Gmp::GroupRecord rec = data.group_records(i); + + qToBigEndian(quint64(rec.group_address().v6_hi()), + (uchar*)(ip.data())); + qToBigEndian(quint64(rec.group_address().v6_lo()), + (uchar*)(ip.data() + 8)); + grpRec["groupRecordAddress"] = QHostAddress( + (quint8*)ip.constData()).toString(); + + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + grpRec["groupRecordSourceList"] = sl; + + grpRecords.replace(i, grpRec); + } + return grpRecords; + } + case FieldFrameValue: + { + QVariantList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toList(); + QByteArray fv; + QByteArray ip; + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QByteArray rv = list.at(i).toByteArray(); + + rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(rv.data()+4)); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(rv.data()+4+8)); + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(rv.data()+20+16*j)); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(rv.data()+20+16*j+8)); + } + + fv.append(rv); + } + return fv; + } + case FieldTextValue: + { + QStringList list = GmpProtocol::fieldData( + index, attrib, streamIndex).toStringList(); + QByteArray ip; + + ip.resize(16); + + for (int i = 0; i < data.group_records_size(); i++) + { + OstProto::Gmp::GroupRecord rec = data.group_records(i); + QString recStr = list.at(i); + QString str; + + qToBigEndian(rec.group_address().v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.group_address().v6_lo(), + (uchar*)(ip.data() + 8)); + str.append(QString("Group: %1").arg( + QHostAddress((quint8*)ip.constData()).toString())); + + str.append("; Sources: "); + QStringList sl; + for (int j = 0; j < rec.sources_size(); j++) + { + qToBigEndian(rec.sources(j).v6_hi(), + (uchar*)(ip.data())); + qToBigEndian(rec.sources(j).v6_lo(), + (uchar*)(ip.data() + 8)); + sl.append(QHostAddress( + (quint8*)ip.constData()).toString()); + } + str.append(sl.join(", ")); + + recStr.replace("XXX", str); + list.replace(i, recStr); + } + return list.join("\n").insert(0, "\n"); + } + default: + break; + } + break; + } + default: + break; + } + + return GmpProtocol::fieldData(index, attrib, streamIndex); +} + +bool MldProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case kGroupAddress: + { + 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.mutable_group_address()->set_v6_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.mutable_group_address()->set_v6_lo(x); + break; + } + + case kSources: + { + QStringList list = value.toStringList(); + + data.clear_sources(); + foreach(QString str, list) + { + OstProto::Gmp::IpAddress *src = data.add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + break; + } + + case kGroupRecords: + { + GmpProtocol::setFieldData(index, value, attrib); + QVariantList list = value.toList(); + + for (int i = 0; i < list.count(); i++) + { + QVariantMap grpRec = list.at(i).toMap(); + OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); + Q_IPV6ADDR addr = QHostAddress( + grpRec["groupRecordAddress"].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); + rec->mutable_group_address()->set_v6_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); + rec->mutable_group_address()->set_v6_lo(x); + + QStringList srcList = grpRec["groupRecordSourceList"] + .toStringList(); + rec->clear_sources(); + foreach (QString str, srcList) + { + OstProto::Gmp::IpAddress *src = rec->add_sources(); + Q_IPV6ADDR addr = QHostAddress(str).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); + src->set_v6_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); + src->set_v6_lo(x); + } + } + + break; + } + + default: + isOk = GmpProtocol::setFieldData(index, value, attrib); + break; + } + +_exit: + return isOk; +} + +quint16 MldProtocol::checksum(int streamIndex) const +{ + return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); +} diff --git a/common/mld.h b/common/mld.h new file mode 100644 index 0000000..5403af6 --- /dev/null +++ b/common/mld.h @@ -0,0 +1,95 @@ +/* +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 +*/ +#ifndef _MLD_H +#define _MLD_H + +#include "gmp.h" +#include "mld.pb.h" + +// MLD uses the same msg type value for 'Query' messages across +// versions despite the fields being different. To distinguish +// Query messages of different versions, we use an additional +// upper byte +enum MldMsgType +{ + kMldV1Query = 0x82, + kMldV1Report = 0x83, + kMldV1Done = 0x84, + + kMldV2Query = 0xFF82, + kMldV2Report = 0x8F +}; + +class MldProtocol : public GmpProtocol +{ +public: + MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~MldProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + virtual bool isSsmReport() const; + virtual bool isQuery() const; + virtual bool isSsmQuery() const; + + virtual quint16 checksum(int streamIndex) const; + +private: + int mrc(int value) const; +}; + +inline bool MldProtocol::isSsmReport() const +{ + return (msgType() == kMldV2Report); +} + +inline bool MldProtocol::isQuery() const +{ + return ((msgType() == kMldV1Query) + || (msgType() == kMldV2Query)); +} + +inline bool MldProtocol::isSsmQuery() const +{ + return (msgType() == kMldV2Query); +} + +inline int MldProtocol::mrc(int value) const +{ + return quint16(value); // TODO: if value > 128, convert to mantissa/exp form +} + +#endif diff --git a/common/mld.proto b/common/mld.proto new file mode 100755 index 0000000..2f491e8 --- /dev/null +++ b/common/mld.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "gmp.proto"; + +package OstProto; + +extend Protocol { + optional Gmp mld = 404; +} diff --git a/common/mldconfig.cpp b/common/mldconfig.cpp new file mode 100644 index 0000000..bc871c3 --- /dev/null +++ b/common/mldconfig.cpp @@ -0,0 +1,109 @@ +/* +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 +*/ + +#include "mldconfig.h" +#include "mld.h" + +#include "ipv6addressdelegate.h" +#include "ipv6addressvalidator.h" + +MldConfigForm::MldConfigForm(QWidget *parent) + : GmpConfigForm(parent) +{ + connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), + SLOT(on_msgTypeCombo_currentIndexChanged(int))); + + msgTypeCombo->setValueMask(0xFF); + msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); + msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); + msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); + msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); + msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); + + _defaultGroupIp = "::"; + _defaultSourceIp = "::"; + + groupAddress->setValidator(new IPv6AddressValidator(this)); + groupRecordAddress->setValidator(new IPv6AddressValidator(this)); + sourceList->setItemDelegate(new IPv6AddressDelegate(this)); + groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); +} + +MldConfigForm::~MldConfigForm() +{ +} + +MldConfigForm* MldConfigForm::createInstance() +{ + return new MldConfigForm; +} + +void MldConfigForm::loadWidget(AbstractProtocol *proto) +{ + GmpConfigForm::loadWidget(proto); + + maxResponseTime->setText( + proto->fieldData( + MldProtocol::kMldMrt, + AbstractProtocol::FieldValue + ).toString()); +} + +void MldConfigForm::storeWidget(AbstractProtocol *proto) +{ + GmpConfigForm::storeWidget(proto); + + proto->setFieldData( + MldProtocol::kMldMrt, + maxResponseTime->text()); +} + +// +// -- private slots +// + +void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) +{ + switch(msgTypeCombo->currentValue()) + { + case kMldV1Query: + case kMldV1Report: + case kMldV1Done: + asmGroup->show(); + ssmWidget->hide(); + break; + + case kMldV2Query: + asmGroup->show(); + ssmWidget->setCurrentIndex(kSsmQueryPage); + ssmWidget->show(); + break; + + case kMldV2Report: + asmGroup->hide(); + ssmWidget->setCurrentIndex(kSsmReportPage); + ssmWidget->show(); + break; + + default: + asmGroup->hide(); + ssmWidget->hide(); + break; + } +} diff --git a/common/mldconfig.h b/common/mldconfig.h new file mode 100644 index 0000000..b25617a --- /dev/null +++ b/common/mldconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ +#ifndef _MLD_CONFIG_H +#define _MLD_CONFIG_H + +#include "gmpconfig.h" + +class MldConfigForm : public GmpConfigForm +{ + Q_OBJECT +public: + MldConfigForm(QWidget *parent = 0); + + virtual ~MldConfigForm(); + + static MldConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_msgTypeCombo_currentIndexChanged(int index); +}; + +#endif diff --git a/common/mldpdml.cpp b/common/mldpdml.cpp new file mode 100644 index 0000000..b17503e --- /dev/null +++ b/common/mldpdml.cpp @@ -0,0 +1,133 @@ +/* +Copyright (C) 2011 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 "mldpdml.h" + +#include "mld.pb.h" + +PdmlMldProtocol::PdmlMldProtocol() +{ + ostProtoId_ = OstProto::Protocol::kMldFieldNumber; + + fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); + fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); + fieldMap_.insert("icmpv6.mld.maximum_response_delay", + OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME + + fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); + fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); + fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME + fieldMap_.insert("icmpv6.mld.nb_sources", + OstProto::Gmp::kSourceCountFieldNumber); + + fieldMap_.insert("icmpv6.mldr.nb_mcast_records", + OstProto::Gmp::kGroupRecordCountFieldNumber); +} + +PdmlProtocol* PdmlMldProtocol::createInstance() +{ + return new PdmlMldProtocol(); +} + +void PdmlMldProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + + mld->set_is_override_rsvd_code(true); + mld->set_is_override_checksum(true); + mld->set_is_override_source_count(true); + mld->set_is_override_group_record_count(true); + + protoSize_ = attributes.value("size").toString().toUInt(&isOk); +} + +void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + bool isOk; + OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); + QString valueHexStr = attributes.value("value").toString(); + + if (name == "icmpv6.type") + { + uint type = valueHexStr.toUInt(&isOk, kBaseHex); + + if ((type == kMldQuery) && (protoSize_ >= 28)) + type = kMldV2Query; + + mld->set_type(type); + } + else if (name == "icmpv6.mld.multicast_address") + { + mld->mutable_group_address()->set_v6_hi( + valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + mld->mutable_group_address()->set_v6_lo( + valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mld.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.record_type") + { + OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); + rec->set_type(OstProto::Gmp::GroupRecord::RecordType( + valueHexStr.toUInt(&isOk, kBaseHex))); + rec->set_is_override_source_count(true); + rec->set_is_override_aux_data_length(true); + } + else if (name == "icmpv6.mldr.mar.aux_data_len") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.nb_sources") + { + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.multicast_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->mutable_group_address(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.source_address") + { + OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( + mld->group_records_size() - 1)->add_sources(); + ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); + ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); + } + else if (name == "icmpv6.mldr.mar.auxiliary_data") + { + QByteArray ba = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + mld->mutable_group_records(mld->group_records_size() - 1)-> + set_aux_data(ba.constData(), ba.size()); + } +} + diff --git a/common/mldpdml.h b/common/mldpdml.h new file mode 100644 index 0000000..0c47fbe --- /dev/null +++ b/common/mldpdml.h @@ -0,0 +1,47 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _MLD_PDML_H +#define _MLD_PDML_H + +#include "pdmlprotocol.h" + +class PdmlMldProtocol : public PdmlProtocol +{ + friend class PdmlIcmp6Protocol; +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlMldProtocol(); +private: + static const uint kMldQuery = 0x82; + static const uint kMldV1Query = 0x82; + static const uint kMldV2Query = 0xFF82; + + uint protoSize_; +}; + +#endif diff --git a/common/ostproto.pro b/common/ostproto.pro new file mode 100644 index 0000000..e4467dc --- /dev/null +++ b/common/ostproto.pro @@ -0,0 +1,114 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT -= gui +QT += network script +LIBS += \ + -lprotobuf + +PROTOS = \ + protocol.proto \ + mac.proto \ + payload.proto \ + eth2.proto \ + dot3.proto \ + llc.proto \ + snap.proto \ + dot2llc.proto \ + dot2snap.proto \ + vlan.proto \ + svlan.proto \ + vlanstack.proto \ + arp.proto \ + ip4.proto \ + ip6.proto \ + ip6over4.proto \ + ip4over6.proto \ + ip4over4.proto \ + ip6over6.proto \ + icmp.proto \ + gmp.proto \ + igmp.proto \ + mld.proto \ + tcp.proto \ + udp.proto \ + textproto.proto \ + userscript.proto \ + hexdump.proto \ + sample.proto + +HEADERS = \ + abstractprotocol.h \ + comboprotocol.h \ + protocolmanager.h \ + protocollist.h \ + protocollistiterator.h \ + streambase.h \ + +HEADERS += \ + mac.h \ + vlan.h \ + svlan.h \ + vlanstack.h \ + eth2.h \ + dot3.h \ + llc.h \ + dot2llc.h \ + snap.h \ + dot2snap.h \ + arp.h \ + ip4.h \ + ip6.h \ + ip4over4.h \ + ip4over6.h \ + ip6over4.h \ + ip6over6.h \ + gmp.h \ + icmp.h \ + igmp.h \ + mld.h \ + tcp.h \ + udp.h \ + textproto.h \ + hexdump.h \ + payload.h \ + sample.h \ + userscript.h + +SOURCES = \ + abstractprotocol.cpp \ + crc32c.cpp \ + protocolmanager.cpp \ + protocollist.cpp \ + protocollistiterator.cpp \ + streambase.cpp \ + +SOURCES += \ + mac.cpp \ + vlan.cpp \ + svlan.cpp \ + eth2.cpp \ + dot3.cpp \ + llc.cpp \ + snap.cpp \ + arp.cpp \ + ip4.cpp \ + ip6.cpp \ + gmp.cpp \ + icmp.cpp \ + igmp.cpp \ + mld.cpp \ + tcp.cpp \ + udp.cpp \ + textproto.cpp \ + hexdump.cpp \ + payload.cpp \ + sample.cpp \ + userscript.cpp + +QMAKE_DISTCLEAN += object_script.* + +#binding.depends = compiler_protobuf_py_make_all +#QMAKE_EXTRA_TARGETS += binding + +include(../protobuf.pri) + diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro new file mode 100644 index 0000000..b075046 --- /dev/null +++ b/common/ostprotogui.pro @@ -0,0 +1,129 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network xml script +INCLUDEPATH += "../extra/qhexedit2/src" +LIBS += \ + -lprotobuf + +FORMS = \ + pcapfileimport.ui \ + +FORMS += \ + mac.ui \ + vlan.ui \ + eth2.ui \ + dot3.ui \ + llc.ui \ + snap.ui \ + arp.ui \ + ip4.ui \ + ip6.ui \ + gmp.ui \ + icmp.ui \ + tcp.ui \ + udp.ui \ + textproto.ui \ + hexdump.ui \ + payload.ui \ + sample.ui \ + userscript.ui + +PROTOS = \ + fileformat.proto + +# TODO: Move fileformat related stuff into a different library - why? +HEADERS = \ + ostprotolib.h \ + abstractfileformat.h \ + fileformat.h \ + ipv4addressdelegate.h \ + ipv6addressdelegate.h \ + pcapfileformat.h \ + pdmlfileformat.h \ + pdmlprotocol.h \ + pdmlprotocols.h \ + pdmlreader.h + +HEADERS += \ + abstractprotocolconfig.h \ + comboprotocolconfig.h \ + protocolwidgetfactory.h \ + macconfig.h \ + vlanconfig.h \ + svlanconfig.h \ + vlanstackconfig.h \ + eth2config.h \ + dot3config.h \ + llcconfig.h \ + dot2llcconfig.h \ + snapconfig.h \ + dot2snapconfig.h \ + arpconfig.h \ + ip4config.h \ + ip6config.h \ + ip4over4config.h \ + gmpconfig.h \ + icmpconfig.h \ + igmpconfig.h \ + mldconfig.h \ + tcpconfig.h \ + udpconfig.h \ + textprotoconfig.h \ + hexdumpconfig.h \ + payloadconfig.h \ + sampleconfig.h \ + userscriptconfig.h + +SOURCES += \ + ostprotolib.cpp \ + abstractfileformat.cpp \ + fileformat.cpp \ + pcapfileformat.cpp \ + pdmlfileformat.cpp \ + pdmlprotocol.cpp \ + pdmlprotocols.cpp \ + pdmlreader.cpp \ + +SOURCES += \ + protocolwidgetfactory.cpp \ + macconfig.cpp \ + vlanconfig.cpp \ + eth2config.cpp \ + dot3config.cpp \ + llcconfig.cpp \ + snapconfig.cpp \ + arpconfig.cpp \ + ip4config.cpp \ + ip6config.cpp \ + gmpconfig.cpp \ + icmpconfig.cpp \ + igmpconfig.cpp \ + mldconfig.cpp \ + tcpconfig.cpp \ + udpconfig.cpp \ + textprotoconfig.cpp \ + hexdumpconfig.cpp \ + payloadconfig.cpp \ + sampleconfig.cpp \ + userscriptconfig.cpp + +SOURCES += \ + vlanpdml.cpp \ + svlanpdml.cpp \ + eth2pdml.cpp \ + llcpdml.cpp \ + arppdml.cpp \ + ip4pdml.cpp \ + ip6pdml.cpp \ + icmppdml.cpp \ + icmp6pdml.cpp \ + igmppdml.cpp \ + mldpdml.cpp \ + tcppdml.cpp \ + udppdml.cpp \ + textprotopdml.cpp \ + samplepdml.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../protobuf.pri) diff --git a/common/ostprotolib.cpp b/common/ostprotolib.cpp new file mode 100644 index 0000000..bd1fcc2 --- /dev/null +++ b/common/ostprotolib.cpp @@ -0,0 +1,55 @@ +/* +Copyright (C) 2011 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 "ostprotolib.h" + +QString OstProtoLib::tsharkPath_; +QString OstProtoLib::gzipPath_; +QString OstProtoLib::diffPath_; +QString OstProtoLib::awkPath_; + +// TODO: one set method for each external app +void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath) +{ + tsharkPath_ = tsharkPath; + gzipPath_ = gzipPath; + diffPath_ = diffPath; + awkPath_ = awkPath; +} + +QString OstProtoLib::tsharkPath() +{ + return tsharkPath_; +} + +QString OstProtoLib::gzipPath() +{ + return gzipPath_; +} + +QString OstProtoLib::diffPath() +{ + return diffPath_; +} + +QString OstProtoLib::awkPath() +{ + return awkPath_; +} diff --git a/common/ostprotolib.h b/common/ostprotolib.h new file mode 100644 index 0000000..4d10626 --- /dev/null +++ b/common/ostprotolib.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _OST_PROTO_LIB_H +#define _OST_PROTO_LIB_H + +#include + +class OstProtoLib +{ +public: + static void setExternalApplicationPaths(QString tsharkPath, + QString gzipPath, QString diffPath, QString awkPath); + + static QString tsharkPath(); + static QString gzipPath(); + static QString diffPath(); + static QString awkPath(); + +private: + static QString tsharkPath_; + static QString gzipPath_; + static QString diffPath_; + static QString awkPath_; +}; + +#endif diff --git a/common/payload.cpp b/common/payload.cpp new file mode 100644 index 0000000..2a7756d --- /dev/null +++ b/common/payload.cpp @@ -0,0 +1,258 @@ +/* +Copyright (C) 2010, 2013-2014 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 "payload.h" +#include "streambase.h" + +PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +PayloadProtocol::~PayloadProtocol() +{ +} + +AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new PayloadProtocol(stream, parent); +} + +quint32 PayloadProtocol::protocolNumber() const +{ + return OstProto::Protocol::kPayloadFieldNumber; +} + +void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::payload)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::payload)) + data.MergeFrom(protocol.GetExtension(OstProto::payload)); +} + +QString PayloadProtocol::name() const +{ + return QString("Payload Data"); +} + +QString PayloadProtocol::shortName() const +{ + return QString("DATA"); +} + +int PayloadProtocol::protocolFrameSize(int streamIndex) const +{ + int len; + + len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) + - kFcsSize; + + if (len < 0) + len = 0; + + qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, + streamIndex, len); + return len; +} + +int PayloadProtocol::fieldCount() const +{ + return payload_fieldCount; +} + +AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case payload_dataPattern: + break; + + // Meta fields + case payload_dataPatternMode: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case payload_dataPattern: + switch(attrib) + { + case FieldName: + return QString("Data"); + case FieldValue: + return data.pattern(); + case FieldTextValue: + return QString(fieldData(index, FieldFrameValue, + streamIndex).toByteArray().toHex()); + case FieldFrameValue: + { + QByteArray fv; + int dataLen; + + dataLen = protocolFrameSize(streamIndex); + + // FIXME: Hack! Bad! Bad! Very Bad!!! + if (dataLen <= 0) + dataLen = 1; + + fv.resize(dataLen+4); + switch(data.pattern_mode()) + { + case OstProto::Payload::e_dp_fixed_word: + for (int i = 0; i < (dataLen/4)+1; i++) + qToBigEndian((quint32) data.pattern(), + (uchar*)(fv.data()+(i*4)) ); + break; + case OstProto::Payload::e_dp_inc_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = i % (0xFF + 1); + break; + case OstProto::Payload::e_dp_dec_byte: + for (int i = 0; i < dataLen; i++) + fv[i] = 0xFF - (i % (0xFF + 1)); + break; + case OstProto::Payload::e_dp_random: + //! \todo (HIGH) cksum is incorrect for random pattern + for (int i = 0; i < dataLen; i++) + fv[i] = qrand() % (0xFF + 1); + break; + default: + qWarning("Unhandled data pattern %d", + data.pattern_mode()); + } + fv.resize(dataLen); + return fv; + } + default: + break; + } + break; + + // Meta fields + + case payload_dataPatternMode: + switch(attrib) + { + case FieldValue: return data.pattern_mode(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool PayloadProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case payload_dataPattern: + { + uint pattern = value.toUInt(&isOk); + if (isOk) + data.set_pattern(pattern); + break; + } + case payload_dataPatternMode: + { + uint mode = value.toUInt(&isOk); + if (isOk && data.DataPatternMode_IsValid(mode)) + data.set_pattern_mode(OstProto::Payload::DataPatternMode(mode)); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; +} + +bool PayloadProtocol::isProtocolFrameValueVariable() const +{ + if (isProtocolFrameSizeVariable() + || data.pattern_mode() == OstProto::Payload::e_dp_random) + return true; + else + return false; +} + +bool PayloadProtocol::isProtocolFrameSizeVariable() const +{ + if (mpStream->lenMode() == StreamBase::e_fl_fixed) + return false; + else + return true; +} + +int PayloadProtocol::protocolFrameVariableCount() const +{ + int count = 1; + + if (data.pattern_mode() == OstProto::Payload::e_dp_random) + { + switch(mpStream->sendUnit()) + { + case OstProto::StreamControl::e_su_packets: + return mpStream->numPackets(); + + case OstProto::StreamControl::e_su_bursts: + return int(mpStream->numBursts() + * mpStream->burstSize() + * mpStream->burstRate()); + } + } + + if (mpStream->lenMode() != StreamBase::e_fl_fixed) + { + count = AbstractProtocol::lcm(count, + mpStream->frameLenMax() - mpStream->frameLenMin() + 1); + } + + return count; +} diff --git a/common/payload.h b/common/payload.h new file mode 100644 index 0000000..4c95da5 --- /dev/null +++ b/common/payload.h @@ -0,0 +1,71 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _PAYLOAD_H +#define _PAYLOAD_H + +#include "abstractprotocol.h" + +#include "payload.pb.h" + +class PayloadProtocol : public AbstractProtocol +{ +public: + enum payloadfield + { + payload_dataPattern, + + // Meta fields + payload_dataPatternMode, + + payload_fieldCount + }; + + PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~PayloadProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Payload data; +}; + +#endif diff --git a/common/payload.proto b/common/payload.proto new file mode 100644 index 0000000..bafa4c3 --- /dev/null +++ b/common/payload.proto @@ -0,0 +1,41 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Payload { + enum DataPatternMode { + e_dp_fixed_word = 0; + e_dp_inc_byte = 1; + e_dp_dec_byte = 2; + e_dp_random = 3; + } + + // Data Pattern + optional DataPatternMode pattern_mode = 1; + optional uint32 pattern = 2; + + //optional uint32 data_start_ofs = 13; +} + +extend Protocol { + optional Payload payload = 101; +} diff --git a/common/payload.ui b/common/payload.ui new file mode 100644 index 0000000..a7ff9a2 --- /dev/null +++ b/common/payload.ui @@ -0,0 +1,106 @@ + + payload + + + + 0 + 0 + 299 + 114 + + + + Form + + + + + + Type + + + cmbPatternMode + + + + + + + + Fixed Word + + + + + Increment Byte + + + + + Decrement Byte + + + + + Random + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pattern + + + lePattern + + + + + + + >HH HH HH HH; + + + + + + 11 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/common/payloadconfig.cpp b/common/payloadconfig.cpp new file mode 100644 index 0000000..d06d672 --- /dev/null +++ b/common/payloadconfig.cpp @@ -0,0 +1,84 @@ +/* +Copyright (C) 2010-2014 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 "payloadconfig.h" + +#include "payload.h" + +PayloadConfigForm::PayloadConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +PayloadConfigForm::~PayloadConfigForm() +{ +} + +AbstractProtocolConfigForm* PayloadConfigForm::createInstance() +{ + return new PayloadConfigForm; +} + +void PayloadConfigForm::loadWidget(AbstractProtocol *proto) +{ + cmbPatternMode->setCurrentIndex( + proto->fieldData( + PayloadProtocol::payload_dataPatternMode, + AbstractProtocol::FieldValue + ).toUInt()); + lePattern->setText(uintToHexStr( + proto->fieldData( + PayloadProtocol::payload_dataPattern, + AbstractProtocol::FieldValue + ).toUInt(), 4)); +} + +void PayloadConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + PayloadProtocol::payload_dataPatternMode, + cmbPatternMode->currentIndex()); + + proto->setFieldData( + PayloadProtocol::payload_dataPattern, + lePattern->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); +} + +void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) +{ + switch(index) + { + case OstProto::Payload::e_dp_fixed_word: + lePattern->setEnabled(true); + break; + case OstProto::Payload::e_dp_inc_byte: + case OstProto::Payload::e_dp_dec_byte: + case OstProto::Payload::e_dp_random: + lePattern->setDisabled(true); + break; + default: + qWarning("Unhandled/Unknown PatternMode = %d",index); + } +} + + + diff --git a/common/payloadconfig.h b/common/payloadconfig.h new file mode 100644 index 0000000..8ebfeb0 --- /dev/null +++ b/common/payloadconfig.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2010-2014 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 +*/ + +#ifndef _PAYLOAD_CONFIG_H +#define _PAYLOAD_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_payload.h" + +class PayloadConfigForm : + public AbstractProtocolConfigForm, + private Ui::payload +{ + Q_OBJECT +public: + PayloadConfigForm(QWidget *parent = 0); + virtual ~PayloadConfigForm(); + + static AbstractProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: + void on_cmbPatternMode_currentIndexChanged(int index); +}; + +#endif diff --git a/common/pcapfileformat.cpp b/common/pcapfileformat.cpp new file mode 100644 index 0000000..e96b684 --- /dev/null +++ b/common/pcapfileformat.cpp @@ -0,0 +1,661 @@ +/* +Copyright (C) 2011 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 "pcapfileformat.h" + +#include "pdmlreader.h" +#include "ostprotolib.h" +#include "streambase.h" +#include "hexdump.pb.h" + +#include +#include +#include +#include +#include +#include + +static inline quint32 swap32(quint32 val) +{ + return (((val >> 24) && 0x000000FF) | + ((val >> 16) && 0x0000FF00) | + ((val << 16) && 0x00FF0000) | + ((val << 24) && 0xFF000000)); +} + +static inline quint16 swap16(quint16 val) +{ + return (((val >> 8) && 0x00FF) | + ((val << 8) && 0xFF00)); +} + +const quint32 kPcapFileMagic = 0xa1b2c3d4; +const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; +const quint16 kPcapFileVersionMajor = 2; +const quint16 kPcapFileVersionMinor = 4; +const quint32 kMaxSnapLen = 65535; +const quint32 kDltEthernet = 1; + +PcapFileFormat pcapFileFormat; + +PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) + : QDialog(NULL) +{ + setupUi(this); + options_ = options; + + viaPdml->setChecked(options_->value("ViaPdml").toBool()); + doDiff->setChecked(options_->value("DoDiff").toBool()); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +PcapImportOptionsDialog::~PcapImportOptionsDialog() +{ +} + +void PcapImportOptionsDialog::accept() +{ + options_->insert("ViaPdml", viaPdml->isChecked()); + options_->insert("DoDiff", doDiff->isChecked()); + + QDialog::accept(); +} + +PcapFileFormat::PcapFileFormat() +{ + importOptions_.insert("ViaPdml", true); + importOptions_.insert("DoDiff", true); + + importDialog_ = NULL; +} + +PcapFileFormat::~PcapFileFormat() +{ + delete importDialog_; +} + +bool PcapFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + QTemporaryFile file2; + quint32 magic; + uchar gzipMagic[2]; + int len; + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + OstProto::Stream *prevStream = NULL; + uint lastUsec = 0; + int pktCount; + qint64 byteCount = 0; + qint64 byteTotal; + QByteArray pktBuf; + + if (!file.open(QIODevice::ReadOnly)) + goto _err_open; + + len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); + if (len < int(sizeof(gzipMagic))) + goto _err_reading_magic; + + if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) + { + QProcess gzip; + + emit status("Decompressing..."); + emit target(0); + + if (!file2.open()) + { + error.append("Unable to open temporary file to uncompress .gz\n"); + goto _err_unzip_fail; + } + + qDebug("decompressing to %s", file2.fileName().toAscii().constData()); + + gzip.setStandardOutputFile(file2.fileName()); + gzip.start(OstProtoLib::gzipPath(), + QStringList() + << "-d" + << "-c" + << fileName); + if (!gzip.waitForStarted(-1)) + { + error.append(QString("Unable to start gzip. Check path in Preferences.\n")); + goto _err_unzip_fail; + } + + if (!gzip.waitForFinished(-1)) + { + error.append(QString("Error running gzip\n")); + goto _err_unzip_fail; + } + + file2.seek(0); + + fd_.setDevice(&file2); + } + else + { + fd_.setDevice(&file); + } + + byteTotal = fd_.device()->size() - sizeof(fileHdr); + + emit status("Reading File Header..."); + emit target(0); + + fd_ >> magic; + + qDebug("magic = %08x", magic); + + if (magic == kPcapFileMagicSwapped) + { + // Toggle Byte order + if (fd_.byteOrder() == QDataStream::BigEndian) + fd_.setByteOrder(QDataStream::LittleEndian); + else + fd_.setByteOrder(QDataStream::BigEndian); + } + else if (magic != kPcapFileMagic) + goto _err_bad_magic; + + fd_ >> fileHdr.versionMajor; + fd_ >> fileHdr.versionMinor; + fd_ >> fileHdr.thisZone; + fd_ >> fileHdr.sigfigs; + fd_ >> fileHdr.snapLen; + fd_ >> fileHdr.network; + + if ((fileHdr.versionMajor != kPcapFileVersionMajor) || + (fileHdr.versionMinor != kPcapFileVersionMinor)) + goto _err_unsupported_version; + +#if 1 + // XXX: we support only Ethernet, for now + if (fileHdr.network != kDltEthernet) + goto _err_unsupported_encap; +#endif + + pktBuf.resize(fileHdr.snapLen); + + if (importOptions_.value("ViaPdml").toBool()) + { + QProcess tshark; + QTemporaryFile pdmlFile; + PdmlReader reader(&streams); + + if (!pdmlFile.open()) + { + error.append("Unable to open temporary file to create PDML\n"); + goto _non_pdml; + } + + qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); + emit status("Generating PDML..."); + emit target(0); + + tshark.setStandardOutputFile(pdmlFile.fileName()); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(fileName) + << "-otcp.desegment_tcp_streams:FALSE" + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _non_pdml; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _non_pdml; + } + + connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Reading PDML packets..."); + emit target(100); // in percentage + isOk = reader.read(&pdmlFile, this, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + goto _exit; + } + + if (!importOptions_.value("DoDiff").toBool()) + goto _exit; + + + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + // Let's do the diff ... + // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! + + QProcess awk; + QProcess diff; + QTemporaryFile originalTextFile; + QTemporaryFile importedPcapFile; + QTemporaryFile importedTextFile; + QTemporaryFile diffFile; + const QString kAwkFilter = + "/^[^0]/ { " + "printf \" %s \", $1;" + "for (i=4; i %s", + originalTextFile.fileName().toAscii().constData(), + importedTextFile.fileName().toAscii().constData(), + diffFile.fileName().toAscii().constData()); + + emit status("Taking diff..."); + emit target(0); + + diff.setStandardOutputFile(diffFile.fileName()); + diff.start(OstProtoLib::diffPath(), + QStringList() + << "-u" + << "-F^ [1-9]" + << QString("--label=%1 (actual)") + .arg(QFileInfo(fileName).fileName()) + << QString("--label=%1 (imported)") + .arg(QFileInfo(fileName).fileName()) + << originalTextFile.fileName() + << importedTextFile.fileName()); + if (!diff.waitForStarted(-1)) + { + error.append(QString("Unable to start diff. Check path in Preferences.\n") + .arg(diff.exitCode())); + goto _diff_fail; + } + + if (!diff.waitForFinished(-1)) + { + error.append(QString("Error running diff\n")); + goto _diff_fail; + } + + diffFile.close(); + if (diffFile.size()) + { + error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); + diffFile.open(); + diffFile.seek(0); + error.append(QString(diffFile.readAll())); + } + + goto _exit; + } + +_non_pdml: + emit status("Reading Packets..."); + emit target(100); // in percentage + pktCount = 1; + while (!fd_.atEnd()) + { + OstProto::Stream *stream = streams.add_stream(); + OstProto::Protocol *proto = stream->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + readPacket(pktHdr, pktBuf); + + // validations on inclLen <= origLen && inclLen <= snapLen + Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if + + hexDump->set_content(pktBuf.data(), pktHdr.inclLen); + hexDump->set_pad_until_end(false); + + stream->mutable_stream_id()->set_id(pktCount); + stream->mutable_core()->set_is_enabled(true); + stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS + + // setup packet rate to the timing in pcap (as close as possible) + const uint kUsecsInSec = uint(1e6); + uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); + uint delta = usec - lastUsec; + + if ((pktCount != 1) && delta) + stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); + + if (prevStream) + prevStream->mutable_control()->CopyFrom(stream->control()); + + lastUsec = usec; + prevStream = stream; + pktCount++; + qDebug("pktCount = %d", pktCount); + byteCount += pktHdr.inclLen + sizeof(pktHdr); + emit progress(int(byteCount*100/byteTotal)); // in percentage + if (stop_) + goto _user_cancel; + } + + isOk = true; + goto _exit; + +_user_cancel: + isOk = true; + goto _exit; + +_diff_fail: + goto _exit; + +_err_unsupported_encap: + error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " + "not supported - Sorry!")) + .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); + goto _exit; + +_err_unsupported_version: + error = QString(tr("%1 is in PCAP version %2.%3 format which is " + "not supported - Sorry!")) + .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); + goto _exit; + +_err_bad_magic: + error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); + goto _exit; + +#if 0 +_err_truncated: + error = QString(tr("%1 is too short")).arg(fileName); + goto _exit; +#endif + +_err_unzip_fail: + goto _exit; + +_err_reading_magic: + error = QString(tr("Unable to read magic from %1")).arg(fileName); + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + file.close(); + return isOk; +} + +/*! + Reads packet meta data into pktHdr and packet content into buf. + + Returns true if packet is read successfully, false otherwise. +*/ +bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) +{ + quint32 len; + + // TODO: chk fd_.status() + + // read PcapPacketHeader + fd_ >> pktHdr.tsSec; + fd_ >> pktHdr.tsUsec; + fd_ >> pktHdr.inclLen; + fd_ >> pktHdr.origLen; + + // TODO: chk fd_.status() + + // XXX: should never be required, but we play safe + if (quint32(pktBuf.size()) < pktHdr.inclLen) + pktBuf.resize(pktHdr.inclLen); + + // read Pkt contents + len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? + + Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert + pktBuf.resize(len); + + return true; +} + +bool PcapFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PcapFileHeader fileHdr; + PcapPacketHeader pktHdr; + QByteArray pktBuf; + + if (!file.open(QIODevice::WriteOnly)) + goto _err_open; + + fd_.setDevice(&file); + + fileHdr.magicNumber = kPcapFileMagic; + fileHdr.versionMajor = kPcapFileVersionMajor; + fileHdr.versionMinor = kPcapFileVersionMinor; + fileHdr.thisZone = 0; + fileHdr.sigfigs = 0; + fileHdr.snapLen = kMaxSnapLen; + fileHdr.network = kDltEthernet; + + fd_ << fileHdr.magicNumber; + fd_ << fileHdr.versionMajor; + fd_ << fileHdr.versionMinor; + fd_ << fileHdr.thisZone; + fd_ << fileHdr.sigfigs; + fd_ << fileHdr.snapLen; + fd_ << fileHdr.network; + + pktBuf.resize(kMaxSnapLen); + + emit status("Writing Packets..."); + emit target(streams.stream_size()); + + pktHdr.tsSec = 0; + pktHdr.tsUsec = 0; + for (int i = 0; i < streams.stream_size(); i++) + { + StreamBase s; + + s.setId(i); + s.protoDataCopyFrom(streams.stream(i)); + // TODO: expand frameIndex for each stream + s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); + + pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 + pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding + + qDebug("savepcap i=%d, incl/orig len = %d/%d", i, + pktHdr.inclLen, pktHdr.origLen); + + if (pktHdr.inclLen > fileHdr.snapLen) + pktHdr.inclLen = fileHdr.snapLen; + + fd_ << pktHdr.tsSec; + fd_ << pktHdr.tsUsec; + fd_ << pktHdr.inclLen; + fd_ << pktHdr.origLen; + fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); + + if (s.packetRate()) + pktHdr.tsUsec += quint32(1e6/s.packetRate()); + if (pktHdr.tsUsec >= 1000000) + { + pktHdr.tsSec++; + pktHdr.tsUsec -= 1000000; + } + + emit progress(i); + } + + file.close(); + + isOk = true; + goto _exit; + +_err_open: + error = QString(tr("Unable to open file: %1")).arg(fileName); + goto _exit; + +_exit: + return isOk; +} + +QDialog* PcapFileFormat::openOptionsDialog() +{ + if (!importDialog_) + importDialog_ = new PcapImportOptionsDialog(&importOptions_); + + return importDialog_; +} + +bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) +{ + // TODO + return true; +} + +bool PcapFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PCAP")) + return true; + else + return false; +} diff --git a/common/pcapfileformat.h b/common/pcapfileformat.h new file mode 100644 index 0000000..064aaf1 --- /dev/null +++ b/common/pcapfileformat.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2011 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 +*/ +#ifndef _PCAP_FILE_FORMAT_H +#define _PCAP_FILE_FORMAT_H + +#include "abstractfileformat.h" +#include "ui_pcapfileimport.h" + +#include +#include + +class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport +{ +public: + PcapImportOptionsDialog(QVariantMap *options); + ~PcapImportOptionsDialog(); + +private slots: + void accept(); + +private: + QVariantMap *options_; +}; + +class PdmlReader; +class PcapFileFormat : public AbstractFileFormat +{ + friend class PdmlReader; + +public: + PcapFileFormat(); + ~PcapFileFormat(); + + bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + virtual QDialog* openOptionsDialog(); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + typedef struct { + quint32 magicNumber; /* magic number */ + quint16 versionMajor; /* major version number */ + quint16 versionMinor; /* minor version number */ + qint32 thisZone; /* GMT to local correction */ + quint32 sigfigs; /* accuracy of timestamps */ + quint32 snapLen; /* max length of captured packets, in octets */ + quint32 network; /* data link type */ + } PcapFileHeader; + + typedef struct { + quint32 tsSec; /* timestamp seconds */ + quint32 tsUsec; /* timestamp microseconds */ + quint32 inclLen; /* number of octets of packet saved in file */ + quint32 origLen; /* actual length of packet */ + } PcapPacketHeader; + + bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); + + QDataStream fd_; + QVariantMap importOptions_; + PcapImportOptionsDialog *importDialog_; +}; + +extern PcapFileFormat pcapFileFormat; + +#endif diff --git a/common/pcapfileimport.ui b/common/pcapfileimport.ui new file mode 100644 index 0000000..8718c45 --- /dev/null +++ b/common/pcapfileimport.ui @@ -0,0 +1,132 @@ + + PcapFileImport + + + + 0 + 0 + 326 + 93 + + + + PCAP import options + + + + + + Intelligent Import (via PDML) + + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + Do a diff after import + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PcapFileImport + accept() + + + 249 + 81 + + + 157 + 90 + + + + + buttonBox + rejected() + PcapFileImport + reject() + + + 249 + 81 + + + 258 + 90 + + + + + viaPdml + toggled(bool) + doDiff + setEnabled(bool) + + + 15 + 16 + + + 37 + 42 + + + + + viaPdml + toggled(bool) + doDiff + setChecked(bool) + + + 151 + 14 + + + 150 + 34 + + + + + diff --git a/common/pdmlfileformat.cpp b/common/pdmlfileformat.cpp new file mode 100644 index 0000000..e567d99 --- /dev/null +++ b/common/pdmlfileformat.cpp @@ -0,0 +1,170 @@ +/* +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 +*/ + +#include "pdmlfileformat.h" + +#include "ostprotolib.h" +#include "pdmlreader.h" + +#include +#include + +PdmlFileFormat pdmlFileFormat; + +PdmlFileFormat::PdmlFileFormat() +{ +} + +PdmlFileFormat::~PdmlFileFormat() +{ +} + +bool PdmlFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + bool isOk = false; + QFile file(fileName); + PdmlReader *reader = new PdmlReader(&streams); + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + emit status("Reading PDML packets..."); + emit target(100); // in percentage + + isOk = reader->read(&file, NULL, &stop_); + + if (stop_) + goto _user_cancel; + + if (!isOk) + { + error.append(QString("Error processing PDML (%1, %2): %3\n") + .arg(reader->lineNumber()) + .arg(reader->columnNumber()) + .arg(reader->errorString())); + goto _exit; + } + + goto _exit; + +_open_fail: + isOk = false; + +_user_cancel: +_exit: + delete reader; + + return isOk; +} + +bool PdmlFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + bool isOk = false; + QTemporaryFile pcapFile; + AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType("PCAP"); + QProcess tshark; + + Q_ASSERT(fmt); + + if (!pcapFile.open()) + { + error.append("Unable to open temporary file to create PCAP\n"); + goto _fail; + } + + qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); + + connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); + connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); + + emit status("Writing intermediate PCAP file..."); + isOk = fmt->saveStreams(streams, pcapFile.fileName(), error); + + qDebug("generating PDML %s", fileName.toAscii().constData()); + emit status("Converting PCAP to PDML..."); + emit target(0); + + tshark.setStandardOutputFile(fileName); + tshark.start(OstProtoLib::tsharkPath(), + QStringList() + << QString("-r%1").arg(pcapFile.fileName()) + << "-Tpdml"); + if (!tshark.waitForStarted(-1)) + { + error.append(QString("Unable to start tshark. Check path in preferences.\n")); + goto _fail; + } + + if (!tshark.waitForFinished(-1)) + { + error.append(QString("Error running tshark\n")); + goto _fail; + } + + isOk = true; +_fail: + return isOk; +} + +bool PdmlFileFormat::isMyFileFormat(const QString fileName) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + QXmlStreamReader xml; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + xml.setDevice(&file); + + xml.readNext(); + if (xml.hasError() || !xml.isStartDocument()) + goto _close_exit; + + // skip everything until the start of the first element + while (!xml.isStartElement()) + { + xml.readNext(); + if (xml.hasError()) + goto _close_exit; + } + + if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) + ret = true; + else + ret = false; + +_close_exit: + xml.clear(); + file.close(); +_exit: + return ret; +} + +bool PdmlFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PDML")) + return true; + else + return false; +} diff --git a/common/pdmlfileformat.h b/common/pdmlfileformat.h new file mode 100644 index 0000000..e05026a --- /dev/null +++ b/common/pdmlfileformat.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#ifndef _PDML_FILE_FORMAT_H +#define _PDML_FILE_FORMAT_H + +#include "abstractfileformat.h" + +class PdmlFileFormat : public AbstractFileFormat +{ +public: + PdmlFileFormat(); + ~PdmlFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +}; + +extern PdmlFileFormat pdmlFileFormat; + +#endif diff --git a/common/pdmlprotocol.cpp b/common/pdmlprotocol.cpp new file mode 100644 index 0000000..a682aeb --- /dev/null +++ b/common/pdmlprotocol.cpp @@ -0,0 +1,248 @@ +/* +Copyright (C) 2011 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 "pdmlprotocol.h" + +/*! + \class PdmlProtocol + + PdmlProtocol is the base class which provides the interface for all + PDML decode helper protocols + + All Pdml helper classes derived from PdmlProtocol MUST register + themselves with PdmlReader. When PdmlReader encounters a 'proto' tag + in the PDML during parsing, it instantiates the corresponding helper + PdmlProtocol class and calls its methods to decode the protocol. + + A subclass MUST initialize the following inherited protected variables + in its constructor - + - ostProtoId_ + - fieldMap_ + + A subclass typically needs to reimplement the following methods - + - createInstance() + + Depending on certain conditions, subclasses may need to reimplement + the following additional methods - + - unknownFieldHandler() + - preProtocolHandler() + - postProtocolHandler() + + See the description of the methods for more information. + + Use the SamplePdmlProtocol implementation as boilerplate code and + for guidelines and tips +*/ + +/*! + Constructs the PdmlProtocol +*/ +PdmlProtocol::PdmlProtocol() +{ + ostProtoId_ = -1; +} + +/*! + Destroys the PdmlProtocol +*/ +PdmlProtocol::~PdmlProtocol() +{ +} + +/*! + Allocates and returns a new instance of the class + + Caller is responsible for freeing up after use. Subclasses MUST implement + this function and register it with PdmlReader +*/ +PdmlProtocol* PdmlProtocol::createInstance() +{ + return new PdmlProtocol(); +} + +/*! + Returns the protocol's field number as defined in message 'Protocol', enum 'k' + (file: protocol.proto) +*/ +int PdmlProtocol::ostProtoId() const +{ + return ostProtoId_; +} + +/*! + Returns true if name is a 'known' field that can be directly mapped + to the protobuf field +*/ +bool PdmlProtocol::hasField(QString name) const +{ + return fieldMap_.contains(name); +} + +/*! + Returns the protocol's protobuf field number corresponding to name +*/ +int PdmlProtocol::fieldId(QString name) const +{ + return fieldMap_.value(name); +} + +/*! + This method is called by PdmlReader before any fields within the protocol + are processed. All attributes associated with the 'proto' tag in the PDML + are passed to this method + + Use this method to do any special handling that may be required for + preprocessing +*/ +void PdmlProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +/*! + This method is called by PdmlReader when it encounters a nested + protocol in the PDML i.e. a protocol within a protocol or a protocol + within a field + + This is a notification to the protocol that protocol processing will + be ending prematurely. postProtocolHandler() will still be called in + such cases. +*/ +void PdmlProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + +/*! + This method is called by PdmlReader after all fields within the protocol + are processed. + + Use this method to do any special handling that may be required for + postprocessing +*/ +void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} + + +/*! + This method is called by PdmlReader for each field in the protocol + + Depending on whether it is a known or unknown field, the virtual methods + knownFieldHandler() and unknownFieldHandler() are invoked +*/ +void PdmlProtocol::fieldHandler(QString name, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (hasField(name)) + { + QString valueHexStr = attributes.value("value").toString(); + + qDebug("\t(KNOWN) fieldName:%s, value:%s", + name.toAscii().constData(), + valueHexStr.toAscii().constData()); + + knownFieldHandler(name, valueHexStr, pbProto); + } + else + { + int pos = -1; + int size = -1; + + if (!attributes.value("pos").isEmpty()) + pos = attributes.value("pos").toString().toInt(); + if (!attributes.value("size").isEmpty()) + size = attributes.value("size").toString().toInt(); + + qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", + name.toAscii().constData(), pos, size); + + unknownFieldHandler(name, pos, size, attributes, pbProto, stream); + } +} + +/*! + Handles a 'known' field + + Uses protobuf reflection interface to set the protobuf field name to + valueHexStr as per the field's datatype +*/ +void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto) +{ + const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); + const google::protobuf::FieldDescriptor *extDesc = + protoRefl->FindKnownExtensionByNumber(ostProtoId()); + + google::protobuf::Message *msg = + protoRefl->MutableMessage(pbProto,extDesc); + + const google::protobuf::Reflection *msgRefl = msg->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); + + bool isOk; + + Q_ASSERT(fieldDesc != NULL); + switch(fieldDesc->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + msgRefl->SetUInt32(msg, fieldDesc, + valueHexStr.toUInt(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + msgRefl->SetUInt64(msg, fieldDesc, + valueHexStr.toULongLong(&isOk, kBaseHex)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); + std::string str(hexVal.constData(), hexVal.size()); + msgRefl->SetString(msg, fieldDesc, str); + break; + } + default: + qDebug("%s: unhandled cpptype = %d", __FUNCTION__, + fieldDesc->cpp_type()); + } +} + +/*! + Handles a 'unknown' field + + The default implementation does nothing. Subclasses may need to implement + this if the protocol contains 'unknown' fields. +*/ +void PdmlProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; // do nothing! +} diff --git a/common/pdmlprotocol.h b/common/pdmlprotocol.h new file mode 100644 index 0000000..011fcb6 --- /dev/null +++ b/common/pdmlprotocol.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOL_H +#define _PDML_PROTOCOL_H + +#include "protocol.pb.h" + +#include +#include +#include +#include + +const int kBaseHex = 16; + +class PdmlProtocol +{ +public: + virtual ~PdmlProtocol(); + + static PdmlProtocol* createInstance(); + + int ostProtoId() const; + bool hasField(QString name) const; + int fieldId(QString name) const; + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + void knownFieldHandler(QString name, QString valueHexStr, + OstProto::Protocol *pbProto); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlProtocol(); + + //!< Protocol's field number as defined in message 'Protocol', enum 'k' + int ostProtoId_; + //!< Map of PDML field names to protobuf field numbers for 'known' fields + QMap fieldMap_; +}; + +#endif diff --git a/common/pdmlprotocols.cpp b/common/pdmlprotocols.cpp new file mode 100644 index 0000000..c64d46d --- /dev/null +++ b/common/pdmlprotocols.cpp @@ -0,0 +1,195 @@ +/* +Copyright (C) 2011 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 "pdmlprotocols.h" + +#include "hexdump.pb.h" + +// ---------------------------------------------------------- // +// PdmlUnknownProtocol // +// ---------------------------------------------------------- // + +PdmlUnknownProtocol::PdmlUnknownProtocol() +{ + ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; + + endPos_ = expPos_ = -1; +} + +PdmlProtocol* PdmlUnknownProtocol::createInstance() +{ + return new PdmlUnknownProtocol(); +} + +void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + OstProto::HexDump *hexDump = stream->mutable_protocol( + stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); + hexDump->set_pad_until_end(false); +} + +void PdmlUnknownProtocol::prematureEndHandler(int pos, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + endPos_ = pos; +} + +void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + // Skipped field(s) at end? Pad with zero! + if (endPos_ > expPos_) + { + QByteArray hexVal(endPos_ - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); + + // If empty for some reason, remove the protocol + if (hexDump->content().size() == 0) + stream->mutable_protocol()->RemoveLast(); + + endPos_ = expPos_ = -1; +} + +void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); + + qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", + name.toAscii().constData(), + pos, expPos_, endPos_); + + // Skipped field? Pad with zero! + if ((pos > expPos_) && (expPos_ < endPos_)) + { + QByteArray hexVal(pos - expPos_, char(0)); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } + + if (pos == expPos_) + { + QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? + QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : + QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); + + hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); + expPos_ += hexVal.size(); + } +} + + +// ---------------------------------------------------------- // +// PdmlGenInfoProtocol // +// ---------------------------------------------------------- // + +PdmlGenInfoProtocol::PdmlGenInfoProtocol() +{ +} + +PdmlProtocol* PdmlGenInfoProtocol::createInstance() +{ + return new PdmlGenInfoProtocol(); +} + +// ---------------------------------------------------------- // +// PdmlFrameProtocol // +// ---------------------------------------------------------- // + +PdmlFrameProtocol::PdmlFrameProtocol() +{ +} + +PdmlProtocol* PdmlFrameProtocol::createInstance() +{ + return new PdmlFrameProtocol(); +} + +void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) +{ + if (name == "frame.len") + { + int len = -1; + + if (!attributes.value("show").isEmpty()) + len = attributes.value("show").toString().toInt(); + stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS + } + else if (name == "frame.time_delta") + { + if (!attributes.value("show").isEmpty()) + { + QString delta = attributes.value("show").toString(); + int decimal = delta.indexOf('.'); + + if (decimal >= 0) + { + const uint kNsecsInSec = 1000000000; + uint sec = delta.left(decimal).toUInt(); + uint nsec = delta.mid(decimal+1).toUInt(); + uint ipg = sec*kNsecsInSec + nsec; + + if (ipg) + { + stream->mutable_control()->set_packets_per_sec( + kNsecsInSec/ipg); + } + + qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); + } + } + } +} diff --git a/common/pdmlprotocols.h b/common/pdmlprotocols.h new file mode 100644 index 0000000..0747df1 --- /dev/null +++ b/common/pdmlprotocols.h @@ -0,0 +1,71 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_PROTOCOLS_H +#define _PDML_PROTOCOLS_H + +#include "pdmlprotocol.h" + +class PdmlUnknownProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlUnknownProtocol(); + +private: + int endPos_; + int expPos_; +}; + +class PdmlGenInfoProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + +protected: + PdmlGenInfoProtocol(); + +}; + +class PdmlFrameProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlFrameProtocol(); +}; + +#endif diff --git a/common/pdmlreader.cpp b/common/pdmlreader.cpp new file mode 100644 index 0000000..7b5eb9d --- /dev/null +++ b/common/pdmlreader.cpp @@ -0,0 +1,548 @@ +/* +Copyright (C) 2011 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 "pdmlreader.h" + +#include "abstractprotocol.h" +#include "hexdump.pb.h" +#include "pcapfileformat.h" +#include "streambase.h" + +#include "pdmlprotocols.h" + +#include "arppdml.h" +#include "eth2pdml.h" +#include "llcpdml.h" +#include "icmppdml.h" +#include "icmp6pdml.h" +#include "igmppdml.h" +#include "ip4pdml.h" +#include "ip6pdml.h" +#include "mldpdml.h" +#include "svlanpdml.h" +#include "tcppdml.h" +#include "textprotopdml.h" +#include "udppdml.h" +#include "vlanpdml.h" + +PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) +{ + //gPdmlReader = this; + pcap_ = NULL; + streams_ = streams; + + currentStream_ = NULL; + prevStream_ = NULL; + + stop_ = NULL; + + factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); + factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); + factory_.insert("frame", PdmlFrameProtocol::createInstance); + + factory_.insert("arp", PdmlArpProtocol::createInstance); + factory_.insert("eth", PdmlEthProtocol::createInstance); + factory_.insert("http", PdmlTextProtocol::createInstance); + factory_.insert("icmp", PdmlIcmpProtocol::createInstance); + factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); + factory_.insert("igmp", PdmlIgmpProtocol::createInstance); + factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); + factory_.insert("imap", PdmlTextProtocol::createInstance); + factory_.insert("ip", PdmlIp4Protocol::createInstance); + factory_.insert("ipv6", PdmlIp6Protocol::createInstance); + factory_.insert("llc", PdmlLlcProtocol::createInstance); + factory_.insert("nntp", PdmlTextProtocol::createInstance); + factory_.insert("pop", PdmlTextProtocol::createInstance); + factory_.insert("rtsp", PdmlTextProtocol::createInstance); + factory_.insert("sdp", PdmlTextProtocol::createInstance); + factory_.insert("sip", PdmlTextProtocol::createInstance); + factory_.insert("smtp", PdmlTextProtocol::createInstance); + factory_.insert("tcp", PdmlTcpProtocol::createInstance); + factory_.insert("udp", PdmlUdpProtocol::createInstance); + factory_.insert("udplite", PdmlUdpProtocol::createInstance); + factory_.insert("vlan", PdmlVlanProtocol::createInstance); +} + +PdmlReader::~PdmlReader() +{ +} + +bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) +{ + setDevice(device); + pcap_ = pcap; + stop_ = stop; + + while (!atEnd()) + { + readNext(); + if (isStartElement()) + { + if (name() == "pdml") + readPdml(); + else + raiseError("Not a pdml file!"); + } + } + + if (error() && (errorString() != "USER-CANCEL")) + { + qDebug("Line %lld", lineNumber()); + qDebug("Col %lld", columnNumber()); + qDebug("%s", errorString().toAscii().constData()); + return false; + } + return true; +} + +// TODO: use a temp pool to avoid a lot of new/delete +PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) +{ + // If protoName is not known, we use a hexdump + if (!factory_.contains(protoName)) + protoName = "hexdump"; + + // If MLD is not supported by the creator of the PDML, we interpret + // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol + // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is + // also handled by ICMP only + if (!isMldSupport_ && (protoName == "icmpv6")) + protoName = "icmp"; + + return (*(factory_.value(protoName)))(); +} + +void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) +{ + delete proto; +} + +bool PdmlReader::isDontCareProto() +{ + Q_ASSERT(isStartElement() && name() == "proto"); + + QStringRef protoName = attributes().value("name"); + + if (protoName.isEmpty() || (protoName == "expert")) + return true; + + return false; +} + +void PdmlReader::skipElement() +{ + Q_ASSERT(isStartElement()); + + qDebug("skipping element - <%s>", + name().toString().toAscii().constData()); + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipElement(); + } +} + +void PdmlReader::readPdml() +{ + QStringList creator; + + Q_ASSERT(isStartElement() && name() == "pdml"); + + isMldSupport_ = true; + creator = attributes().value("creator").toString().split('/'); + if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) + { + QList minMldVer; + minMldVer << 1 << 5 << 0; + QStringList version = creator.at(1).split('.'); + + for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) + { + if (version.at(i).toUInt() < minMldVer.at(i)) + { + isMldSupport_ = false; + break; + } + } + } + + packetCount_ = 1; + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "packet") + readPacket(); + else + skipElement(); + } + } +} + +void PdmlReader::readPacket() +{ + PcapFileFormat::PcapPacketHeader pktHdr; + + Q_ASSERT(isStartElement() && name() == "packet"); + + qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); + + skipUntilEnd_ = false; + + // XXX: we play dumb and convert each packet to a stream, for now + prevStream_ = currentStream_; + currentStream_ = streams_->add_stream(); + currentStream_->mutable_stream_id()->set_id(packetCount_); + currentStream_->mutable_core()->set_is_enabled(true); + + // Set to a high number; will get reset to correct value during parse + currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! + + expPos_ = 0; + + if (pcap_) + pcap_->readPacket(pktHdr, pktBuf_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (skipUntilEnd_) + skipElement(); + else if (name() == "proto") + readProto(); + else if (name() == "field") + readField(NULL, NULL); // TODO: top level field!!!! + else + skipElement(); + } + } + + currentStream_->mutable_core()->set_name(""); // FIXME + + // If trailing bytes are missing, add those from the pcap + if ((expPos_ < pktBuf_.size()) && pcap_) + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension( + OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("adding trailing %d bytes starting from %d", + pktBuf_.size() - expPos_, expPos_); + hexDump->set_content(pktBuf_.constData() + expPos_, + pktBuf_.size() - expPos_); + hexDump->set_pad_until_end(false); + } + + packetCount_++; + emit progress(int(characterOffset()*100/device()->size())); // in % + if (prevStream_) + prevStream_->mutable_control()->CopyFrom(currentStream_->control()); + if (stop_ && *stop_) + raiseError("USER-CANCEL"); +} + +void PdmlReader::readProto() +{ + PdmlProtocol *pdmlProto = NULL; + OstProto::Protocol *pbProto = NULL; + + Q_ASSERT(isStartElement() && name() == "proto"); + + QString protoName; + int pos = -1; + int size = -1; + + if (!attributes().value("name").isEmpty()) + protoName = attributes().value("name").toString(); + if (!attributes().value("pos").isEmpty()) + pos = attributes().value("pos").toString().toInt(); + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", + protoName.toAscii().constData(), pos, expPos_, size); + + // This is a heuristic to skip protocols which are not part of + // this frame, but of a reassembled segment spanning several frames + // 1. Proto starting pos is 0, but we've already seen some protocols + // 2. Protocol Size exceeds frame length + if (((pos == 0) && (currentStream_->protocol_size() > 0)) + || ((pos + size) > int(currentStream_->core().frame_len()))) + { + skipElement(); + return; + } + + if (isDontCareProto()) + { + skipElement(); + return; + } + + // if we detect a gap between subsequent protocols, we "fill-in" + // with a "hexdump" from the pcap + if (pos > expPos_ && pcap_) + { + appendHexDumpProto(expPos_, pos - expPos_); + expPos_ = pos; + } + + // for unknown protocol, read a hexdump from the pcap + if (!factory_.contains(protoName) && pcap_) + { + int size = -1; + + if (!attributes().value("size").isEmpty()) + size = attributes().value("size").toString().toInt(); + + // Check if this proto is a subset of previous proto - if so, do nothing + if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) + { + qDebug("subset proto"); + skipElement(); + return; + } + + if (pos >= 0 && size > 0 + && ((pos + size) <= pktBuf_.size())) + { + appendHexDumpProto(pos, size); + expPos_ += size; + + skipElement(); + return; + } + } + + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, + currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // an embedded proto + qDebug("embedded proto: %s\n", attributes().value("name") + .toString().toAscii().constData()); + + if (isDontCareProto()) + { + skipElement(); + continue; + } + + // if we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + // XXX: pdmlProto may be NULL for a sequence of embedded protos + if (pdmlProto) + { + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } + + readProto(); + + pdmlProto = NULL; + pbProto = NULL; + } + else if (name() == "field") + { + if ((protoName == "fake-field-wrapper") && + (attributes().value("name") == "tcp.segments")) + { + skipElement(); + qDebug("[skipping reassembled tcp segments]"); + + skipUntilEnd_ = true; + continue; + } + + if (pdmlProto == NULL) + { + pdmlProto = appendPdmlProto(protoName, &pbProto); + + qDebug("%s: preProtocolHandler(expPos = %d)", + protoName.toAscii().constData(), expPos_); + pdmlProto->preProtocolHandler(protoName, attributes(), + expPos_, pbProto, currentStream_); + } + + readField(pdmlProto, pbProto); + } + else + skipElement(); + } + } + + // Close-off current protocol + if (pdmlProto) + { + pdmlProto->postProtocolHandler(pbProto, currentStream_); + freePdmlProtocol(pdmlProto); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + } +} + +void PdmlReader::readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto) +{ + Q_ASSERT(isStartElement() && name() == "field"); + + // fields with "hide='yes'" are informational and should be skipped + if (attributes().value("hide") == "yes") + { + skipElement(); + return; + } + + QString fieldName = attributes().value("name").toString(); + + qDebug(" fieldName:%s", fieldName.toAscii().constData()); + + pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); + + while (!atEnd()) + { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + { + if (name() == "proto") + { + // Since we are in the midst of processing a protocol, we + // end it prematurely before we start processing the + // embedded protocol + // + int endPos = -1; + + if (!attributes().value("pos").isEmpty()) + endPos = attributes().value("pos").toString().toInt(); + + pdmlProto->prematureEndHandler(endPos, pbProto, + currentStream_); + pdmlProto->postProtocolHandler(pbProto, currentStream_); + + StreamBase s; + s.protoDataCopyFrom(*currentStream_); + expPos_ = s.frameProtocolLength(0); + + readProto(); + } + else if (name() == "field") + readField(pdmlProto, pbProto); + else + skipElement(); + } + } +} + +void PdmlReader::appendHexDumpProto(int offset, int size) +{ + OstProto::Protocol *proto = currentStream_->add_protocol(); + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + qDebug("filling in gap of %d bytes starting from %d", size, offset); + hexDump->set_content(pktBuf_.constData() + offset, size); + hexDump->set_pad_until_end(false); +} + +PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto) +{ + PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); + Q_ASSERT(pdmlProto != NULL); + + int protoId = pdmlProto->ostProtoId(); + + if (protoId > 0) // Non-Base Class + { + OstProto::Protocol *proto = currentStream_->add_protocol(); + + proto->mutable_protocol_id()->set_id(protoId); + + const google::protobuf::Reflection *msgRefl = proto->GetReflection(); + const google::protobuf::FieldDescriptor *fieldDesc = + msgRefl->FindKnownExtensionByNumber(protoId); + + // TODO: if !fDesc + // init default values of all fields in protocol + msgRefl->MutableMessage(proto, fieldDesc); + + *pbProto = proto; + + qDebug("%s: name = %s", __FUNCTION__, + protoName.toAscii().constData()); + } + else + *pbProto = NULL; + + return pdmlProto; +} diff --git a/common/pdmlreader.h b/common/pdmlreader.h new file mode 100644 index 0000000..7de3918 --- /dev/null +++ b/common/pdmlreader.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _PDML_READER_H +#define _PDML_READER_H + +#include "pdmlprotocol.h" + +#include +#include + +class PcapFileFormat; +class PdmlReader : public QObject, public QXmlStreamReader +{ + Q_OBJECT +public: + PdmlReader(OstProto::StreamConfigList *streams); + ~PdmlReader(); + + bool read(QIODevice *device, PcapFileFormat *pcap = NULL, + bool *stop = NULL); +signals: + void progress(int value); + +private: + PdmlProtocol* allocPdmlProtocol(QString protoName); + void freePdmlProtocol(PdmlProtocol *proto); + + bool isDontCareProto(); + void skipElement(); + + void readPdml(); + void readPacket(); + void readProto(); + void readField(PdmlProtocol *pdmlProto, + OstProto::Protocol *pbProto); + + void appendHexDumpProto(int offset, int size); + PdmlProtocol* appendPdmlProto(const QString &protoName, + OstProto::Protocol **pbProto); + + typedef PdmlProtocol* (*FactoryMethod)(); + + QMap factory_; + + bool *stop_; + OstProto::StreamConfigList *streams_; + PcapFileFormat *pcap_; + QByteArray pktBuf_; + + bool isMldSupport_; + int packetCount_; + int expPos_; + bool skipUntilEnd_; + OstProto::Stream *prevStream_; + OstProto::Stream *currentStream_; +}; + +#endif diff --git a/common/protocol.proto b/common/protocol.proto new file mode 100644 index 0000000..2039bb4 --- /dev/null +++ b/common/protocol.proto @@ -0,0 +1,265 @@ +/* +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 +*/ + +package OstProto; +option cc_generic_services = true; +option py_generic_services = true; + +message VersionInfo { + required string version = 1; +} + +message VersionCompatibility { + enum Compatibility { + kIncompatible = 0; + kCompatible = 1; + } + required Compatibility result = 1; + optional string notes = 2; +} + +message StreamId { + required uint32 id = 1; +} + +message StreamCore { + enum FrameLengthMode { + e_fl_fixed = 0; + e_fl_inc = 1; + e_fl_dec = 2; + e_fl_random = 3; + } + + // Basics + optional string name = 1; + optional bool is_enabled = 2; + optional uint32 ordinal = 3; + + // Frame Length (includes CRC) + optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; + optional uint32 frame_len = 15 [default = 64]; + optional uint32 frame_len_min = 16 [default = 64]; + optional uint32 frame_len_max = 17 [default = 1518]; +} + +message StreamControl { + enum SendUnit { + e_su_packets = 0; + e_su_bursts = 1; + } + + enum SendMode { + e_sm_fixed = 0; + e_sm_continuous = 1; + } + + enum NextWhat { + e_nw_stop = 0; + e_nw_goto_next = 1; + e_nw_goto_id = 2; + } + + optional SendUnit unit = 1 [default = e_su_packets]; + optional SendMode mode = 2 [default = e_sm_fixed]; + optional uint32 num_packets = 3 [default = 1]; + optional uint32 num_bursts = 4 [default = 1]; + optional uint32 packets_per_burst = 5 [default = 10]; + optional NextWhat next = 6 [default = e_nw_goto_next]; + optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; + optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; + optional double packets_per_sec = 9 [default = 1]; + optional double bursts_per_sec = 10 [default = 1]; +} + +message ProtocolId { + required uint32 id = 1; +} + +message Protocol { + + required ProtocolId protocol_id = 1; + + extensions 100 to 199; // Reserved for Ostinato Use + extensions 200 to 500; // Available for use by protocols + + enum k { + kMacFieldNumber = 100; + kPayloadFieldNumber = 101; + kSampleFieldNumber = 102; + kUserScriptFieldNumber = 103; + kHexDumpFieldNumber = 104; + + kEth2FieldNumber = 200; + kDot3FieldNumber = 201; + kLlcFieldNumber = 202; + kSnapFieldNumber = 203; + + kSvlanFieldNumber = 204; + kVlanFieldNumber = 205; + + kDot2LlcFieldNumber = 206; + kDot2SnapFieldNumber = 207; + kVlanStackFieldNumber = 208; + + kArpFieldNumber = 300; + kIp4FieldNumber = 301; + kIp6FieldNumber = 302; + kIp6over4FieldNumber = 303; + kIp4over6FieldNumber = 304; + kIp4over4FieldNumber = 305; + kIp6over6FieldNumber = 306; + + kTcpFieldNumber = 400; + kUdpFieldNumber = 401; + kIcmpFieldNumber = 402; + kIgmpFieldNumber = 403; + kMldFieldNumber = 404; + + kTextProtocolFieldNumber = 500; + } +} + +message Stream { + + required StreamId stream_id = 1; + optional StreamCore core = 2; + optional StreamControl control = 3; + + repeated Protocol protocol = 4; +} + +message Void { + // nothing! +} + +message Ack { + //! \todo (LOW) do we need any fields in 'Ack' +} + +message PortId { + required uint32 id = 1; +} + +message PortIdList { + repeated PortId port_id = 1; +} + +message StreamIdList { + required PortId port_id = 1; + repeated StreamId stream_id = 2; +} + +enum TransmitMode { + kSequentialTransmit = 0; + kInterleavedTransmit = 1; +} + +message Port { + required PortId port_id = 1; + optional string name = 2; + optional string description = 3; + optional string notes = 4; + optional bool is_enabled = 5; + optional bool is_exclusive_control = 6; + optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; +} + +message PortConfigList { + repeated Port port = 1; +} + +message StreamConfigList { + required PortId port_id = 1; + repeated Stream stream = 2; +} + +message CaptureBuffer { + //! \todo (HIGH) define CaptureBuffer +} + +message CaptureBufferList { + repeated CaptureBuffer list = 1; +} + +enum LinkState { + LinkStateUnknown = 0; + LinkStateDown = 1; + LinkStateUp = 2; +} + +message PortState { + optional LinkState link_state = 1 [default = LinkStateUnknown]; + optional bool is_transmit_on = 2 [default = false]; + optional bool is_capture_on = 3 [default = false]; +} + +message PortStats { + + required PortId port_id = 1; + + optional PortState state = 2; + + optional uint64 rx_pkts = 11; + optional uint64 rx_bytes = 12; + optional uint64 rx_pkts_nic = 13; + optional uint64 rx_bytes_nic = 14; + optional uint64 rx_pps = 15; + optional uint64 rx_bps = 16; + + optional uint64 tx_pkts = 21; + optional uint64 tx_bytes = 22; + optional uint64 tx_pkts_nic = 23; + optional uint64 tx_bytes_nic = 24; + optional uint64 tx_pps = 25; + optional uint64 tx_bps = 26; + + optional uint64 rx_drops = 100; + optional uint64 rx_errors = 101; + optional uint64 rx_fifo_errors = 102; + optional uint64 rx_frame_errors = 103; +} + +message PortStatsList { + repeated PortStats port_stats = 1; +} + +service OstService { + rpc getPortIdList(Void) returns (PortIdList); + rpc getPortConfig(PortIdList) returns (PortConfigList); + rpc modifyPort(PortConfigList) returns (Ack); + + rpc getStreamIdList(PortId) returns (StreamIdList); + rpc getStreamConfig(StreamIdList) returns (StreamConfigList); + rpc addStream(StreamIdList) returns (Ack); + rpc deleteStream(StreamIdList) returns (Ack); + rpc modifyStream(StreamConfigList) returns (Ack); + + rpc startTransmit(PortIdList) returns (Ack); + rpc stopTransmit(PortIdList) returns (Ack); + + rpc startCapture(PortIdList) returns (Ack); + rpc stopCapture(PortIdList) returns (Ack); + rpc getCaptureBuffer(PortId) returns (CaptureBuffer); + + rpc getStats(PortIdList) returns (PortStatsList); + rpc clearStats(PortIdList) returns (Ack); + + rpc checkVersion(VersionInfo) returns (VersionCompatibility); +} + diff --git a/common/protocollist.cpp b/common/protocollist.cpp new file mode 100644 index 0000000..1b3397c --- /dev/null +++ b/common/protocollist.cpp @@ -0,0 +1,27 @@ +/* +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 +*/ + +#include "protocollist.h" +#include "abstractprotocol.h" + +void ProtocolList::destroy() +{ + while (!isEmpty()) + delete takeFirst(); +} diff --git a/common/protocollist.h b/common/protocollist.h new file mode 100644 index 0000000..62df3c9 --- /dev/null +++ b/common/protocollist.h @@ -0,0 +1,28 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; + +class ProtocolList : public QLinkedList +{ +public: + void destroy(); +}; diff --git a/common/protocollistiterator.cpp b/common/protocollistiterator.cpp new file mode 100644 index 0000000..9f82c3d --- /dev/null +++ b/common/protocollistiterator.cpp @@ -0,0 +1,133 @@ +/* +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 +*/ + +#include "protocollistiterator.h" +#include "protocollist.h" +#include "abstractprotocol.h" + +ProtocolListIterator::ProtocolListIterator(ProtocolList &list) +{ + _iter = new QMutableLinkedListIterator(list); +} + +ProtocolListIterator::~ProtocolListIterator() +{ + delete _iter; +} + +bool ProtocolListIterator::findNext(const AbstractProtocol* value) const +{ + return _iter->findNext(const_cast(value)); +} + +bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) +{ + return _iter->findPrevious(const_cast(value)); +} + +bool ProtocolListIterator::hasNext() const +{ + return _iter->hasNext(); +} + +bool ProtocolListIterator::hasPrevious() const +{ + return _iter->hasPrevious(); +} + +void ProtocolListIterator::insert(AbstractProtocol* value) +{ + if (_iter->hasPrevious()) + { + value->prev = _iter->peekPrevious(); + value->prev->next = value; + } + else + value->prev = NULL; + + if (_iter->hasNext()) + { + value->next = _iter->peekNext(); + value->next->prev = value; + } + else + value->next = NULL; + + _iter->insert(const_cast(value)); +} + +AbstractProtocol* ProtocolListIterator::next() +{ + return _iter->next(); +} + +AbstractProtocol* ProtocolListIterator::peekNext() const +{ + return _iter->peekNext(); +} + +AbstractProtocol* ProtocolListIterator::peekPrevious() const +{ + return _iter->peekPrevious(); +} + +AbstractProtocol* ProtocolListIterator::previous() +{ + return _iter->previous(); +} + +void ProtocolListIterator::remove() +{ + if (_iter->value()->prev) + _iter->value()->prev->next = _iter->value()->next; + if (_iter->value()->next) + _iter->value()->next->prev = _iter->value()->prev; + _iter->remove(); +} + +void ProtocolListIterator::setValue(AbstractProtocol* value) const +{ + if (_iter->value()->prev) + _iter->value()->prev->next = value; + if (_iter->value()->next) + _iter->value()->next->prev = value; + value->prev = _iter->value()->prev; + value->next = _iter->value()->next; + _iter->setValue(const_cast(value)); +} + +void ProtocolListIterator::toBack() +{ + _iter->toBack(); +} + +void ProtocolListIterator::toFront() +{ + _iter->toFront(); +} + +const AbstractProtocol* ProtocolListIterator::value() const +{ + return _iter->value(); +} + +AbstractProtocol* ProtocolListIterator::value() +{ + return _iter->value(); +} diff --git a/common/protocollistiterator.h b/common/protocollistiterator.h new file mode 100644 index 0000000..6baa39f --- /dev/null +++ b/common/protocollistiterator.h @@ -0,0 +1,48 @@ +/* +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 +*/ + +#include + +class AbstractProtocol; +class ProtocolList; + +class ProtocolListIterator +{ +private: + QMutableLinkedListIterator *_iter; + +public: + ProtocolListIterator(ProtocolList &list); + ~ProtocolListIterator(); + bool findNext(const AbstractProtocol* value) const; + bool findPrevious(const AbstractProtocol* value); + bool hasNext() const; + bool hasPrevious() const; + void insert(AbstractProtocol* value); + AbstractProtocol* next(); + AbstractProtocol* peekNext() const; + AbstractProtocol* peekPrevious() const; + AbstractProtocol* previous(); + void remove(); + void setValue(AbstractProtocol* value) const; + void toBack(); + void toFront(); + const AbstractProtocol* value() const; + AbstractProtocol* value(); +}; diff --git a/common/protocolmanager.cpp b/common/protocolmanager.cpp new file mode 100644 index 0000000..84e1e2d --- /dev/null +++ b/common/protocolmanager.cpp @@ -0,0 +1,232 @@ +/* +Copyright (C) 2010, 2014 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 "protocolmanager.h" +#include "abstractprotocol.h" + +#include "protocol.pb.h" + +#include "mac.h" +#include "vlan.h" +#include "svlan.h" +#include "vlanstack.h" + +// L2 Protos +#include "dot3.h" +#include "llc.h" +#include "dot2llc.h" +#include "snap.h" +#include "dot2snap.h" +#include "eth2.h" + +// L3 Protos +#include "arp.h" +#include "ip4.h" +#include "ip6.h" +#include "ip4over4.h" +#include "ip4over6.h" +#include "ip6over4.h" +#include "ip6over6.h" + +// L4 Protos +#include "icmp.h" +#include "igmp.h" +#include "mld.h" +#include "tcp.h" +#include "udp.h" + +// L5 Protos +#include "textproto.h" + +// Special Protos +#include "hexdump.h" +#include "payload.h" +#include "sample.h" +#include "userscript.h" + +ProtocolManager *OstProtocolManager; + +ProtocolManager::ProtocolManager() +{ + /*! \todo (LOW) calls to registerProtocol() should be done by the protocols + themselves (once this is done remove the #includes for all the protocols) + */ + registerProtocol(OstProto::Protocol::kMacFieldNumber, + (void*) MacProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kVlanFieldNumber, + (void*) VlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanProtocol::createInstance); + registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackProtocol::createInstance); + + registerProtocol(OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2Protocol::createInstance); + registerProtocol(OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3Protocol::createInstance); + registerProtocol(OstProto::Protocol::kLlcFieldNumber, + (void*) LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSnapFieldNumber, + (void*) SnapProtocol::createInstance); + registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapProtocol::createInstance); + + // Layer 3 Protocols + registerProtocol(OstProto::Protocol::kArpFieldNumber, + (void*) ArpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6Protocol::createInstance); + + registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4Protocol::createInstance); + registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6Protocol::createInstance); + + // Layer 4 Protocols + registerProtocol(OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kMldFieldNumber, + (void*) MldProtocol::createInstance); + registerProtocol(OstProto::Protocol::kTcpFieldNumber, + (void*) TcpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUdpFieldNumber, + (void*) UdpProtocol::createInstance); + + // Layer 5 Protocols + registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocol::createInstance); + + // Special Protocols + registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpProtocol::createInstance); + registerProtocol(OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadProtocol::createInstance); + registerProtocol(OstProto::Protocol::kSampleFieldNumber, + (void*) SampleProtocol::createInstance); + registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptProtocol::createInstance); + + populateNeighbourProtocols(); +} + +ProtocolManager::~ProtocolManager() +{ + numberToNameMap.clear(); + nameToNumberMap.clear(); + neighbourProtocols.clear(); + factory.clear(); + QList pl = protocolList.values(); + while (!pl.isEmpty()) + delete pl.takeFirst(); +} + +void ProtocolManager::registerProtocol(int protoNumber, + void *protoInstanceCreator) +{ + AbstractProtocol *p; + + Q_ASSERT(!factory.contains(protoNumber)); + + factory.insert(protoNumber, protoInstanceCreator); + + p = createProtocol(protoNumber, NULL); + protocolList.insert(protoNumber, p); + + numberToNameMap.insert(protoNumber, p->shortName()); + nameToNumberMap.insert(p->shortName(), protoNumber); +} + +void ProtocolManager::populateNeighbourProtocols() +{ + neighbourProtocols.clear(); + + foreach(AbstractProtocol *p, protocolList) + { + if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) + { + foreach(AbstractProtocol *q, protocolList) + { + if (q->protocolId(p->protocolIdType())) + neighbourProtocols.insert( + p->protocolNumber(), q->protocolNumber()); + } + } + } +} + +bool ProtocolManager::isRegisteredProtocol(int protoNumber) +{ + return factory.contains(protoNumber); +} + +AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, + StreamBase *stream, AbstractProtocol *parent) +{ + AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); + AbstractProtocol* p; + + pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) + factory.value(protoNumber); + + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + QString("No Protocol Creator registered for protocol %1") + .arg(protoNumber).toAscii().constData()); + + p = (*pc)(stream, parent); + + return p; +} + +AbstractProtocol* ProtocolManager::createProtocol(QString protoName, + StreamBase *stream, AbstractProtocol *parent) +{ + return createProtocol(nameToNumberMap.value(protoName), stream, parent); +} + +bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) +{ + if (neighbourProtocols.contains(protoPrefix, protoSuffix)) + return true; + else + return false; +} + +bool ProtocolManager::protocolHasPayload(int protoNumber) +{ + Q_ASSERT(protocolList.contains(protoNumber)); + + return protocolList.value(protoNumber)->protocolHasPayload(); +} + +QStringList ProtocolManager::protocolDatabase() +{ + return numberToNameMap.values(); +} diff --git a/common/protocolmanager.h b/common/protocolmanager.h new file mode 100644 index 0000000..ff7279b --- /dev/null +++ b/common/protocolmanager.h @@ -0,0 +1,58 @@ +/* +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 +*/ + +#ifndef _PROTOCOL_MANAGER_H +#define _PROTOCOL_MANAGER_H + +#include +#include + +class AbstractProtocol; +class StreamBase; + +class ProtocolManager +{ + QMap numberToNameMap; + QMap nameToNumberMap; + QMultiMap neighbourProtocols; + QMap factory; + QMap protocolList; + + void populateNeighbourProtocols(); + +public: + ProtocolManager(); + ~ProtocolManager(); + + // TODO: make registerProtocol static + void registerProtocol(int protoNumber, void *protoInstanceCreator); + + bool isRegisteredProtocol(int protoNumber); + AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, + AbstractProtocol *parent = 0); + AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, + AbstractProtocol *parent = 0); + + bool isValidNeighbour(int protoPrefix, int protoSuffix); + bool protocolHasPayload(int protoNumber); + + QStringList protocolDatabase(); +}; + +#endif diff --git a/common/protocolwidgetfactory.cpp b/common/protocolwidgetfactory.cpp new file mode 100644 index 0000000..8c8e30a --- /dev/null +++ b/common/protocolwidgetfactory.cpp @@ -0,0 +1,193 @@ +/* +Copyright (C) 2014 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 "protocolwidgetfactory.h" + +#include "macconfig.h" +#include "vlanconfig.h" +#include "svlanconfig.h" +#include "vlanstackconfig.h" +// L2 Protocol Widgets +#include "eth2config.h" +#include "dot3config.h" +#include "llcconfig.h" +#include "dot2llcconfig.h" +#include "snapconfig.h" +#include "dot2snapconfig.h" +// L3 Protocol Widgets +#include "arpconfig.h" +#include "ip4config.h" +#include "ip6config.h" +#include "ip4over4config.h" +#include "ip4over6config.h" +#include "ip6over4config.h" +#include "ip6over6config.h" +// L4 Protocol Widgets +#include "icmpconfig.h" +#include "igmpconfig.h" +#include "mldconfig.h" +#include "tcpconfig.h" +#include "udpconfig.h" +// L5 Protocol Widgets +#include "textprotoconfig.h" +// Special Protocol Widgets +#include "hexdumpconfig.h" +#include "payloadconfig.h" +#include "sampleconfig.h" +#include "userscriptconfig.h" + +ProtocolWidgetFactory *OstProtocolWidgetFactory; +QMap ProtocolWidgetFactory::configWidgetFactory; + +ProtocolWidgetFactory::ProtocolWidgetFactory() +{ + /*! + * Ideally Protocol Widgets should register themselves + * with the Factory + */ + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMacFieldNumber, + (void*) MacConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanFieldNumber, + (void*) VlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSvlanFieldNumber, + (void*) SVlanConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kVlanStackFieldNumber, + (void*) VlanStackConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kEth2FieldNumber, + (void*) Eth2ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot3FieldNumber, + (void*) Dot3ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kLlcFieldNumber, + (void*) LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2LlcFieldNumber, + (void*) Dot2LlcConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSnapFieldNumber, + (void*) SnapConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kDot2SnapFieldNumber, + (void*) Dot2SnapConfigForm::createInstance); + + // Layer 3 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kArpFieldNumber, + (void*) ArpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4FieldNumber, + (void*) Ip4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6FieldNumber, + (void*) Ip6ConfigForm::createInstance); + + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over4FieldNumber, + (void*) Ip4over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp4over6FieldNumber, + (void*) Ip4over6ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over4FieldNumber, + (void*) Ip6over4ConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIp6over6FieldNumber, + (void*) Ip6over6ConfigForm::createInstance); + + // Layer 4 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIcmpFieldNumber, + (void*) IcmpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kIgmpFieldNumber, + (void*) IgmpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kMldFieldNumber, + (void*) MldConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTcpFieldNumber, + (void*) TcpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUdpFieldNumber, + (void*) UdpConfigForm::createInstance); + + // Layer 5 Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kTextProtocolFieldNumber, + (void*) TextProtocolConfigForm::createInstance); + + // Special Protocols + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kHexDumpFieldNumber, + (void*) HexDumpConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kPayloadFieldNumber, + (void*) PayloadConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kSampleFieldNumber, + (void*) SampleConfigForm::createInstance); + OstProtocolWidgetFactory->registerProtocolConfigWidget( + OstProto::Protocol::kUserScriptFieldNumber, + (void*) UserScriptConfigForm::createInstance); +} + +ProtocolWidgetFactory::~ProtocolWidgetFactory() +{ + configWidgetFactory.clear(); +} + +void ProtocolWidgetFactory::registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator) +{ + Q_ASSERT(!configWidgetFactory.contains(protoNumber)); + + configWidgetFactory.insert(protoNumber, protoConfigWidgetInstanceCreator); +} + +AbstractProtocolConfigForm* ProtocolWidgetFactory::createConfigWidget( + int protoNumber) +{ + AbstractProtocolConfigForm* (*pc)(); + AbstractProtocolConfigForm* p; + + pc = (AbstractProtocolConfigForm* (*)()) + configWidgetFactory.value(protoNumber); + + Q_ASSERT_X(pc != NULL, + __FUNCTION__, + QString(protoNumber).toAscii().constData()); + + p = (*pc)(); + + return p; +} + +void ProtocolWidgetFactory::deleteConfigWidget( + AbstractProtocolConfigForm *configWidget) +{ + delete configWidget; +} diff --git a/common/protocolwidgetfactory.h b/common/protocolwidgetfactory.h new file mode 100644 index 0000000..ebb69ec --- /dev/null +++ b/common/protocolwidgetfactory.h @@ -0,0 +1,46 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _PROTOCOL_WIDGET_FACTORY_H +#define _PROTOCOL_WIDGET_FACTORY_H + +#include + +class AbstractProtocolConfigForm; + +// Singleton class +class ProtocolWidgetFactory +{ + static QMap configWidgetFactory; + +public: + ProtocolWidgetFactory(); + ~ProtocolWidgetFactory(); + + // TODO: make registerProtocolConfigWidget static?? + // TODO: define a function pointer prototype instead of void* for + // protoConfigWidgetInstanceCreator + static void registerProtocolConfigWidget(int protoNumber, + void *protoConfigWidgetInstanceCreator); + + AbstractProtocolConfigForm* createConfigWidget(int protoNumber); + void deleteConfigWidget(AbstractProtocolConfigForm *configWidget); +}; + +#endif diff --git a/common/sample.cpp b/common/sample.cpp new file mode 100644 index 0000000..0432cf2 --- /dev/null +++ b/common/sample.cpp @@ -0,0 +1,477 @@ +/* +Copyright (C) 2010, 2014 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 "sample.h" + +SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +SampleProtocol::~SampleProtocol() +{ +} + +AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SampleProtocol(stream, parent); +} + +quint32 SampleProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSampleFieldNumber; +} + +void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::sample)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::sample)) + data.MergeFrom(protocol.GetExtension(OstProto::sample)); +} + +QString SampleProtocol::name() const +{ + return QString("Sample Protocol"); +} + +QString SampleProtocol::shortName() const +{ + return QString("SAMPLE"); +} + +/*! + TODO Return the ProtocolIdType for your protocol \n + + If your protocol doesn't have a protocolId field, you don't need to + reimplement this method - the base class implementation will do the + right thing +*/ +AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const +{ + return ProtocolIdIp; +} + +/*! + TODO Return the protocolId for your protoocol based on the 'type' requested \n + + If not all types are valid for your protocol, handle the valid type(s) + and for the remaining fallback to the base class implementation; if your + protocol doesn't have a protocolId at all, you don't need to reimplement + this method - the base class will do the right thing +*/ +quint32 SampleProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xFFFFFF; + case ProtocolIdEth: return 0xFFFF; + case ProtocolIdIp: return 0xFF; + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int SampleProtocol::fieldCount() const +{ + return sample_fieldCount; +} + +/*! + TODO Return the number of frame fields for your protocol. A frame field + is a field which has the FrameField flag set \n + + If your protocol has different sets of fields based on a OpCode/Type field + (e.g. icmp), you MUST re-implement this function; however, if your protocol + has a fixed set of frame fields always, you don't need to reimplement this + method - the base class implementation will do the right thing +*/ +int SampleProtocol::frameFieldCount() const +{ + return AbstractProtocol::frameFieldCount(); +} + +/*! + TODO Edit this function to return the appropriate flags for each field \n + + See AbstractProtocol::FieldFlags for more info +*/ +AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case sample_a: + case sample_b: + case sample_payloadLength: + break; + + case sample_checksum: + flags |= CksumField; + break; + + case sample_x: + case sample_y: + break; + + case sample_is_override_checksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +/*! +TODO: Edit this function to return the data for each field + +See AbstractProtocol::fieldData() for more info +*/ +QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case sample_a: + { + int a = data.ab() >> 13; + + switch(attrib) + { + case FieldName: + return QString("A"); + case FieldValue: + return a; + case FieldTextValue: + return QString("%1").arg(a); + case FieldFrameValue: + return QByteArray(1, (char) a); + case FieldBitSize: + return 3; + default: + break; + } + break; + + } + case sample_b: + { + int b = data.ab() & 0x1FFF; + + switch(attrib) + { + case FieldName: + return QString("B"); + case FieldValue: + return b; + case FieldTextValue: + return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) b, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 13; + default: + break; + } + break; + } + + case sample_payloadLength: + { + switch(attrib) + { + case FieldName: + return QString("Payload Length"); + case FieldValue: + return protocolFramePayloadSize(streamIndex); + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = protocolFramePayloadSize(streamIndex); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("%1").arg( + protocolFramePayloadSize(streamIndex)); + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_checksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_checksum()) + cksum = data.checksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumIp); + break; + default: + cksum = 0; // avoid the 'maybe used unitialized' warning + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1").arg( + cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case sample_x: + { + switch(attrib) + { + case FieldName: + return QString("X"); + case FieldValue: + return data.x(); + case FieldTextValue: + // Use the following line for display in decimal + return QString("%1").arg(data.x()); + // Use the following line for display in hexa-decimal + //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.x(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case sample_y: + { + switch(attrib) + { + case FieldName: + return QString("Y"); + case FieldValue: + return data.y(); + case FieldTextValue: + // Use the following line for display in decimal + //return QString("%1").arg(data.y()); + // Use the following line for display in hexa-decimal + return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.y(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + + // Meta fields + case sample_is_override_checksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_checksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +/*! +TODO: Edit this function to set the data for each field + +See AbstractProtocol::setFieldData() for more info +*/ +bool SampleProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case sample_a: + { + uint a = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0x1FFF) | ((a & 0x07) << 13)); + break; + } + case sample_b: + { + uint b = value.toUInt(&isOk); + if (isOk) + data.set_ab((data.ab() & 0xe000) | (b & 0x1FFF)); + break; + } + case sample_payloadLength: + { + uint len = value.toUInt(&isOk); + if (isOk) + data.set_payload_length(len); + break; + } + case sample_checksum: + { + uint csum = value.toUInt(&isOk); + if (isOk) + data.set_checksum(csum); + break; + } + case sample_x: + { + uint x = value.toUInt(&isOk); + if (isOk) + data.set_x(x); + break; + } + case sample_y: + { + uint y = value.toUInt(&isOk); + if (isOk) + data.set_y(y); + break; + } + case sample_is_override_checksum: + { + bool ovr = value.toBool(); + data.set_is_override_checksum(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +/*! + TODO: Return the protocol frame size in bytes\n + + If your protocol has a fixed size - you don't need to reimplement this; the + base class implementation is good enough +*/ +int SampleProtocol::protocolFrameSize(int streamIndex) const +{ + return AbstractProtocol::protocolFrameSize(streamIndex); +} + +/*! + TODO: If your protocol has any variable fields, return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameValueVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame size can vary across pkts of the same stream, + return true \n + + Otherwise you don't need to reimplement this method - the base class always + returns false +*/ +bool SampleProtocol::isProtocolFrameSizeVariable() const +{ + return false; +} + +/*! + TODO: If your protocol frame has any variable fields or has a variable + size, return the minimum number of frames required to vary the fields \n + + Otherwise you don't need to reimplement this method - the base class always + returns 1 +*/ +int SampleProtocol::protocolFrameVariableCount() const +{ + return 1; +} diff --git a/common/sample.h b/common/sample.h new file mode 100644 index 0000000..475e72f --- /dev/null +++ b/common/sample.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +#include "abstractprotocol.h" +#include "sample.pb.h" + +/* +Sample Protocol Frame Format - + +-----+------+------+------+------+------+ + | A | B | LEN | CSUM | X | Y | + | (3) | (13) | (16) | (16) | (32) | (32) | + +-----+------+------+------+------+------+ +Figures in brackets represent field width in bits +*/ + +class SampleProtocol : public AbstractProtocol +{ +public: + enum samplefield + { + // Frame Fields + sample_a = 0, + sample_b, + sample_payloadLength, + sample_checksum, + sample_x, + sample_y, + + // Meta Fields + sample_is_override_checksum, + + sample_fieldCount + }; + + SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SampleProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + virtual int frameFieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Sample data; +}; + +#endif diff --git a/common/sample.proto b/common/sample.proto new file mode 100644 index 0000000..eeebfda --- /dev/null +++ b/common/sample.proto @@ -0,0 +1,38 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message Sample { + + optional bool is_override_checksum = 1; + + optional uint32 ab = 2; + optional uint32 payload_length = 3; + optional uint32 checksum = 4; + optional uint32 x = 5 [default = 1234]; + optional uint32 y = 6; +} + +extend Protocol { + optional Sample sample = 102; +} diff --git a/common/sample.ui b/common/sample.ui new file mode 100644 index 0000000..2932014 --- /dev/null +++ b/common/sample.ui @@ -0,0 +1,191 @@ + + Sample + + + + 0 + 0 + 263 + 116 + + + + Form + + + + + + Field A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleA + + + + + + + >HH; + + + + + + + + + + Checksum + + + + + + + false + + + >HH HH; + + + + + + + Field B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleB + + + + + + + >HH HH; + + + + + + + Field X + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleX + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + samplePayloadLength + + + + + + + false + + + + + + + + + + Field Y + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleY + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + sampleA + sampleB + samplePayloadLength + isChecksumOverride + sampleChecksum + sampleX + sampleY + + + + + isChecksumOverride + toggled(bool) + sampleChecksum + setEnabled(bool) + + + 345 + 122 + + + 406 + 122 + + + + + diff --git a/common/sampleconfig.cpp b/common/sampleconfig.cpp new file mode 100644 index 0000000..caaf0d2 --- /dev/null +++ b/common/sampleconfig.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2010, 2014 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 "sampleconfig.h" +#include "sample.h" + +SampleConfigForm::SampleConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SampleConfigForm::~SampleConfigForm() +{ +} + +SampleConfigForm* SampleConfigForm::createInstance() +{ + return new SampleConfigForm; +} + +/*! +TODO: Edit this function to load each field's data into the config Widget + +See AbstractProtocolConfigForm::loadWidget() for more info +*/ +void SampleConfigForm::loadWidget(AbstractProtocol *proto) +{ + sampleA->setText( + proto->fieldData( + SampleProtocol::sample_a, + AbstractProtocol::FieldValue + ).toString()); + sampleB->setText( + proto->fieldData( + SampleProtocol::sample_b, + AbstractProtocol::FieldValue + ).toString()); + + samplePayloadLength->setText( + proto->fieldData( + SampleProtocol::sample_payloadLength, + AbstractProtocol::FieldValue + ).toString()); + + isChecksumOverride->setChecked( + proto->fieldData( + SampleProtocol::sample_is_override_checksum, + AbstractProtocol::FieldValue + ).toBool()); + sampleChecksum->setText(uintToHexStr( + proto->fieldData( + SampleProtocol::sample_checksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + + sampleX->setText( + proto->fieldData( + SampleProtocol::sample_x, + AbstractProtocol::FieldValue + ).toString()); + sampleY->setText( + proto->fieldData( + SampleProtocol::sample_y, + AbstractProtocol::FieldValue + ).toString()); +} + +/*! +TODO: Edit this function to store each field's data from the config Widget + +See AbstractProtocolConfigForm::storeWidget() for more info +*/ +void SampleConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SampleProtocol::sample_a, + sampleA->text()); + proto->setFieldData( + SampleProtocol::sample_b, + sampleB->text()); + + proto->setFieldData( + SampleProtocol::sample_payloadLength, + samplePayloadLength->text()); + proto->setFieldData( + SampleProtocol::sample_is_override_checksum, + + isChecksumOverride->isChecked()); + proto->setFieldData( + SampleProtocol::sample_checksum, + hexStrToUInt(sampleChecksum->text())); + + proto->setFieldData( + SampleProtocol::sample_x, + sampleX->text()); + proto->setFieldData( + SampleProtocol::sample_y, + sampleY->text()); +} + diff --git a/common/sampleconfig.h b/common/sampleconfig.h new file mode 100644 index 0000000..8e85976 --- /dev/null +++ b/common/sampleconfig.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _SAMPLE_CONFIG_H +#define _SAMPLE_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_sample.h" + +class SampleConfigForm : + public AbstractProtocolConfigForm, + private Ui::Sample +{ + Q_OBJECT +public: + SampleConfigForm(QWidget *parent = 0); + virtual ~SampleConfigForm(); + + static SampleConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private slots: +}; + +#endif diff --git a/common/samplepdml.cpp b/common/samplepdml.cpp new file mode 100644 index 0000000..99b671b --- /dev/null +++ b/common/samplepdml.cpp @@ -0,0 +1,118 @@ +/* +Copyright (C) 2014 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 "samplepdml.h" + +#include "sample.pb.h" + +/*! + TODO : Initialize the following inherited protected members - + - ostProtoId_ + - fieldMap_ + + ostProtoId_ is the protocol's protobuf field number as defined in + message 'Protocol' enum 'k' in file protocol.proto + + fieldMap_ is a mapping of the protocol's field names as they appear + in the PDML to the protobuf field numbers for the protocol. All such + fields are classified as 'known' fields and the base class will take care + of decoding these without any help from the subclass. + + Note that the PDML field names are same as the field names used in Wireshark + display filters. The full reference for these is available at - + http://www.wireshark.org/docs/dfref/ +*/ +PdmlSampleProtocol::PdmlSampleProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; + + fieldMap_.insert("sample.checksum", OstProto::Sample::kChecksumFieldNumber); + fieldMap_.insert("sample.x", OstProto::Sample::kXFieldNumber); + fieldMap_.insert("sample.y", OstProto::Sample::kYFieldNumber); +} + +PdmlSampleProtocol::~PdmlSampleProtocol() +{ +} + +PdmlSampleProtocol* PdmlSampleProtocol::createInstance() +{ + return new PdmlSampleProtocol(); +} + +/*! + TODO: Use this method to do any special handling that may be required for + preprocessing a protocol before parsing/decoding the protocol's fields +*/ +void PdmlSampleProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, + int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling or cleanup that may be + required when a protocol decode is ending prematurely +*/ +void PdmlSampleProtocol::prematureEndHandler(int /*pos*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Use this method to do any special handling that may be required for + postprocessing a protocol after parsing/decoding all the protocol fields + + If your protocol's protobuf has some meta-fields that should be set to + their non default values, this is a good place to do that. e.g. derived + fields such as length, checksum etc. may be correct or incorrect in the + PCAP/PDML - to retain the same value as in the PCAP/PDML and not let + Ostinato recalculate these, you can set the is_override_length, + is_override_cksum meta-fields to true here +*/ +void PdmlSampleProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, + OstProto::Stream* /*stream*/) +{ + return; +} + +/*! + TODO: Handle all 'unknown' fields using this method + + You need to typically only handle frame fields or fields actually present + in the protocol on the wire. So you can safely ignore meta-fields such as + Good/Bad Checksum. + + Some fields may not have a 'name' attribute, so cannot be classified as + a 'known' field. Use this method to identify such fields using other + attributes such as 'show' or 'showname' and populate the corresponding + protobuf field. + + If the PDML protocol contains some fields that are not supported by Ostinato, + use a HexDump protocol as a replacement to store these bytes +*/ +void PdmlSampleProtocol::unknownFieldHandler(QString /*name*/, + int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, + OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) +{ + return; +} diff --git a/common/samplepdml.h b/common/samplepdml.h new file mode 100644 index 0000000..a07965b --- /dev/null +++ b/common/samplepdml.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SAMPLE_PDML_H +#define _SAMPLE_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSampleProtocol : public PdmlProtocol +{ +public: + virtual ~PdmlSampleProtocol(); + + static PdmlSampleProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, + OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); + + void fieldHandler(QString name, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + +protected: + PdmlSampleProtocol(); +}; + +#endif diff --git a/common/snap.cpp b/common/snap.cpp new file mode 100644 index 0000000..6e7e7cc --- /dev/null +++ b/common/snap.cpp @@ -0,0 +1,255 @@ +/* +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 +*/ + +#include "snap.h" + +quint32 kStdOui = 0x000000; + +SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +SnapProtocol::~SnapProtocol() +{ +} + +AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SnapProtocol(stream, parent); +} + +quint32 SnapProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSnapFieldNumber; +} + +void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::snap)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::snap)) + data.MergeFrom(protocol.GetExtension(OstProto::snap)); +} + +QString SnapProtocol::name() const +{ + return QString("SubNetwork Access Protocol"); +} + +QString SnapProtocol::shortName() const +{ + return QString("SNAP"); +} + +AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const +{ + return ProtocolIdEth; +} + +quint32 SnapProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdLlc: return 0xAAAA03; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int SnapProtocol::fieldCount() const +{ + return snap_fieldCount; +} + +AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case snap_oui: + case snap_type: + break; + + case snap_is_override_oui: + case snap_is_override_type: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + break; + } + + return flags; +} + +QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case snap_oui: + switch(attrib) + { + case FieldName: + return QString("OUI"); + case FieldValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return oui; + } + case FieldTextValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; + QByteArray fv; + fv.resize(4); + qToBigEndian(oui, (uchar*) fv.data()); + fv.remove(0, 1); + return fv; + } + default: + break; + } + break; + case snap_type: + { + quint16 type; + + switch(attrib) + { + case FieldName: + return QString("Type"); + case FieldValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return type; + case FieldTextValue: + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + type = data.is_override_type() ? + data.type() : payloadProtocolId(ProtocolIdEth); + qToBigEndian(type, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + // Meta fields + case snap_is_override_oui: + { + switch(attrib) + { + case FieldValue: + return data.is_override_oui(); + default: + break; + } + break; + } + case snap_is_override_type: + { + switch(attrib) + { + case FieldValue: + return data.is_override_type(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool SnapProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + return false; + + switch (index) + { + case snap_oui: + { + uint oui = value.toUInt(&isOk); + if (isOk) + data.set_oui(oui); + break; + } + case snap_type: + { + uint type = value.toUInt(&isOk); + if (isOk) + data.set_type(type); + break; + } + case snap_is_override_oui: + { + bool ovr = value.toBool(); + data.set_is_override_oui(ovr); + isOk = true; + break; + } + case snap_is_override_type: + { + bool ovr = value.toBool(); + data.set_is_override_type(ovr); + isOk = true; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + return isOk; + +} diff --git a/common/snap.h b/common/snap.h new file mode 100644 index 0000000..bf3a349 --- /dev/null +++ b/common/snap.h @@ -0,0 +1,70 @@ +/* +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 +*/ + +#ifndef _SNAP_H +#define _SNAP_H + +#include "abstractprotocol.h" + +#include "snap.pb.h" + +class SnapProtocol : public AbstractProtocol +{ +public: + enum snapfield + { + snap_oui = 0, + snap_type, + + // Meta fields + snap_is_override_oui, + snap_is_override_type, + + snap_fieldCount + }; + + SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SnapProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +private: + OstProto::Snap data; +}; + +#endif diff --git a/common/snap.proto b/common/snap.proto new file mode 100644 index 0000000..26c607c --- /dev/null +++ b/common/snap.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +message Snap { + optional bool is_override_oui = 3; + optional bool is_override_type = 4; + + optional uint32 oui = 1; + optional uint32 type = 2; +} + +extend Protocol { + optional Snap snap = 203; +} diff --git a/common/snap.ui b/common/snap.ui new file mode 100644 index 0000000..374c7a8 --- /dev/null +++ b/common/snap.ui @@ -0,0 +1,122 @@ + + snap + + + + 0 + 0 + 268 + 98 + + + + Form + + + + + + SNAP + + + + + + OUI + + + + + + + false + + + >HH HH HH; + + + + + + + Type + + + + + + + false + + + >HH HH; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbOverrideOui + toggled(bool) + leOui + setEnabled(bool) + + + 49 + 42 + + + 68 + 43 + + + + + cbOverrideType + toggled(bool) + leType + setEnabled(bool) + + + 161 + 34 + + + 183 + 33 + + + + + diff --git a/common/snapconfig.cpp b/common/snapconfig.cpp new file mode 100644 index 0000000..e594b57 --- /dev/null +++ b/common/snapconfig.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "snapconfig.h" +#include "snap.h" + +SnapConfigForm::SnapConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +SnapConfigForm::~SnapConfigForm() +{ +} + +SnapConfigForm* SnapConfigForm::createInstance() +{ + return new SnapConfigForm; +} + +void SnapConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbOverrideOui->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_oui, + AbstractProtocol::FieldValue + ).toBool()); + leOui->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_oui, + AbstractProtocol::FieldValue + ).toUInt(), 3)); + + cbOverrideType->setChecked( + proto->fieldData( + SnapProtocol::snap_is_override_type, + AbstractProtocol::FieldValue + ).toBool()); + leType->setText(uintToHexStr( + proto->fieldData( + SnapProtocol::snap_type, + AbstractProtocol::FieldValue + ).toUInt(), 2)); +} + +void SnapConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + SnapProtocol::snap_is_override_oui, + cbOverrideOui->isChecked()); + proto->setFieldData( + SnapProtocol::snap_oui, + hexStrToUInt(leOui->text())); + + proto->setFieldData( + SnapProtocol::snap_is_override_type, + cbOverrideType->isChecked()); + proto->setFieldData( + SnapProtocol::snap_type, + hexStrToUInt(leType->text())); +} diff --git a/common/snapconfig.h b/common/snapconfig.h new file mode 100644 index 0000000..9d230e3 --- /dev/null +++ b/common/snapconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _SNAP_CONFIG_H +#define _SNAP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_snap.h" + +class SnapConfigForm : + public AbstractProtocolConfigForm, + private Ui::snap +{ + Q_OBJECT +public: + SnapConfigForm(QWidget *parent = 0); + virtual ~SnapConfigForm(); + + static SnapConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/streambase.cpp b/common/streambase.cpp new file mode 100644 index 0000000..e27b233 --- /dev/null +++ b/common/streambase.cpp @@ -0,0 +1,567 @@ +/* +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 +*/ + +#include "streambase.h" +#include "abstractprotocol.h" +#include "protocollist.h" +#include "protocollistiterator.h" +#include "protocolmanager.h" + +extern ProtocolManager *OstProtocolManager; + +StreamBase::StreamBase() : + mStreamId(new OstProto::StreamId), + mCore(new OstProto::StreamCore), + mControl(new OstProto::StreamControl) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->set_id(0xFFFFFFFF); + + currentFrameProtocols = new ProtocolList; + + iter = createProtocolListIterator(); + // By default newly created streams have the mac and payload protocols + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kMacFieldNumber, this); + iter->insert(proto); + qDebug("stream: mac = %p", proto); + + proto = OstProtocolManager->createProtocol( + OstProto::Protocol::kPayloadFieldNumber, this); + iter->insert(proto); + qDebug("stream: payload = %p", proto); + +#ifndef QT_NO_DEBUG_OUTPUT + { + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{{%p}}", iter->next()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + iter->toFront(); + while (iter->hasNext()) + { + qDebug("{[%d]}", iter->next()->protocolNumber()); + // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); + } + } +#endif + + delete iter; +} + +StreamBase::~StreamBase() +{ + currentFrameProtocols->destroy(); + delete currentFrameProtocols; + delete mControl; + delete mCore; + delete mStreamId; +} + +void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) +{ + AbstractProtocol *proto; + ProtocolListIterator *iter; + + mStreamId->CopyFrom(stream.stream_id()); + mCore->CopyFrom(stream.core()); + mControl->CopyFrom(stream.control()); + + currentFrameProtocols->destroy(); + iter = createProtocolListIterator(); + for (int i=0; i < stream.protocol_size(); i++) + { + int protoId = stream.protocol(i).protocol_id().id(); + + if (!OstProtocolManager->isRegisteredProtocol(protoId)) + { + qWarning("Skipping unregistered protocol %d", protoId); + continue; + } + proto = OstProtocolManager->createProtocol(protoId, this); + proto->protoDataCopyFrom(stream.protocol(i)); + iter->insert(proto); + } + + delete iter; +} + +void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const +{ + stream.mutable_stream_id()->CopyFrom(*mStreamId); + stream.mutable_core()->CopyFrom(*mCore); + stream.mutable_control()->CopyFrom(*mControl); + + stream.clear_protocol(); + foreach (const AbstractProtocol* proto, *currentFrameProtocols) + { + OstProto::Protocol *p; + + p = stream.add_protocol(); + proto->protoDataCopyInto(*p); + } +} + +#if 0 +ProtocolList StreamBase::frameProtocol() +{ + return currentFrameProtocols; +} + +void StreamBase::setFrameProtocol(ProtocolList protocolList) +{ + //currentFrameProtocols.destroy(); + currentFrameProtocols = protocolList; +} +#endif + +ProtocolListIterator* StreamBase::createProtocolListIterator() const +{ + return new ProtocolListIterator(*currentFrameProtocols); +} + +quint32 StreamBase::id() +{ + return mStreamId->id(); +} + +bool StreamBase::setId(quint32 id) +{ + mStreamId->set_id(id); + return true; +} + +quint32 StreamBase::ordinal() +{ + return mCore->ordinal(); +} + +bool StreamBase::setOrdinal(quint32 ordinal) +{ + mCore->set_ordinal(ordinal); + return true; +} + +bool StreamBase::isEnabled() const +{ + return mCore->is_enabled(); +} + +bool StreamBase::setEnabled(bool flag) +{ + mCore->set_is_enabled(flag); + return true; +} + +const QString StreamBase::name() const +{ + return QString().fromStdString(mCore->name()); +} + +bool StreamBase::setName(QString name) +{ + mCore->set_name(name.toStdString()); + return true; +} + +StreamBase::FrameLengthMode StreamBase::lenMode() const +{ + return (StreamBase::FrameLengthMode) mCore->len_mode(); +} + +bool StreamBase::setLenMode(FrameLengthMode lenMode) +{ + mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); + return true; +} + +quint16 StreamBase::frameLen(int streamIndex) const +{ + int pktLen; + + // Decide a frame length based on length mode + switch(lenMode()) + { + case OstProto::StreamCore::e_fl_fixed: + pktLen = mCore->frame_len(); + break; + case OstProto::StreamCore::e_fl_inc: + pktLen = frameLenMin() + (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_dec: + pktLen = frameLenMax() - (streamIndex % + (frameLenMax() - frameLenMin() + 1)); + break; + case OstProto::StreamCore::e_fl_random: + //! \todo (MED) This 'random' sequence is same across iterations + pktLen = 64; // to avoid the 'maybe used uninitialized' warning + qsrand(reinterpret_cast(this)); + for (int i = 0; i <= streamIndex; i++) + pktLen = qrand(); + pktLen = frameLenMin() + (pktLen % + (frameLenMax() - frameLenMin() + 1)); + break; + default: + qWarning("Unhandled len mode %d. Using default 64", + lenMode()); + pktLen = 64; + break; + } + + return pktLen; +} + +bool StreamBase::setFrameLen(quint16 frameLen) +{ + mCore->set_frame_len(frameLen); + return true; +} + +quint16 StreamBase::frameLenMin() const +{ + return mCore->frame_len_min(); +} + +bool StreamBase::setFrameLenMin(quint16 frameLenMin) +{ + mCore->set_frame_len_min(frameLenMin); + return true; +} + +quint16 StreamBase::frameLenMax() const +{ + return mCore->frame_len_max(); +} + +bool StreamBase::setFrameLenMax(quint16 frameLenMax) +{ + mCore->set_frame_len_max(frameLenMax); + return true; +} + +/*! Convenience Function */ +quint16 StreamBase::frameLenAvg() const +{ + quint16 avgFrameLen; + + if (lenMode() == e_fl_fixed) + avgFrameLen = frameLen(); + else + avgFrameLen = (frameLenMin() + frameLenMax())/2; + + return avgFrameLen; +} + +StreamBase::SendUnit StreamBase::sendUnit() const +{ + return (StreamBase::SendUnit) mControl->unit(); +} + +bool StreamBase::setSendUnit(SendUnit sendUnit) +{ + mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); + return true; +} + +StreamBase::SendMode StreamBase::sendMode() const +{ + return (StreamBase::SendMode) mControl->mode(); +} + +bool StreamBase::setSendMode(SendMode sendMode) +{ + mControl->set_mode( + (OstProto::StreamControl::SendMode) sendMode); + return true; +} + +StreamBase::NextWhat StreamBase::nextWhat() const +{ + return (StreamBase::NextWhat) mControl->next(); +} + +bool StreamBase::setNextWhat(NextWhat nextWhat) +{ + mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); + return true; +} + +quint32 StreamBase::numPackets() const +{ + return (quint32) mControl->num_packets(); +} + +bool StreamBase::setNumPackets(quint32 numPackets) +{ + mControl->set_num_packets(numPackets); + return true; +} + +quint32 StreamBase::numBursts() const +{ + return (quint32) mControl->num_bursts(); +} + +bool StreamBase::setNumBursts(quint32 numBursts) +{ + mControl->set_num_bursts(numBursts); + return true; +} + +quint32 StreamBase::burstSize() const +{ + return (quint32) mControl->packets_per_burst(); +} + +bool StreamBase::setBurstSize(quint32 packetsPerBurst) +{ + mControl->set_packets_per_burst(packetsPerBurst); + return true; +} + +double StreamBase::packetRate() const +{ + return (double) mControl->packets_per_sec(); +} + +bool StreamBase::setPacketRate(double packetsPerSec) +{ + mControl->set_packets_per_sec(packetsPerSec); + return true; +} + +double StreamBase::burstRate() const +{ + return (double) mControl->bursts_per_sec(); +} + +bool StreamBase::setBurstRate(double burstsPerSec) +{ + mControl->set_bursts_per_sec(burstsPerSec); + return true; +} + +/*! Convenience Function */ +double StreamBase::averagePacketRate() const +{ + double avgPacketRate = 0; + + switch (sendUnit()) + { + case e_su_bursts: + avgPacketRate = burstRate() * burstSize(); + break; + case e_su_packets: + avgPacketRate = packetRate(); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return avgPacketRate; +} + +/*! Convenience Function */ +bool StreamBase::setAveragePacketRate(double packetsPerSec) +{ + switch (sendUnit()) + { + case e_su_bursts: + setBurstRate(packetsPerSec/double(burstSize())); + break; + case e_su_packets: + setPacketRate(packetsPerSec); + break; + default: + Q_ASSERT(false); // Unreachable!! + } + + return true; +} + +bool StreamBase::isFrameVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameValueVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +bool StreamBase::isFrameSizeVariable() const +{ + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + + proto = iter->next(); + if (proto->isProtocolFrameSizeVariable()) + goto _exit; + } + delete iter; + return false; + +_exit: + delete iter; + return true; +} + +int StreamBase::frameVariableCount() const +{ + ProtocolListIterator *iter; + quint64 frameCount = 1; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + int count; + + proto = iter->next(); + count = proto->protocolFrameVariableCount(); + + // correct count for mis-behaving protocols + if (count <= 0) + count = 1; + + frameCount = AbstractProtocol::lcm(frameCount, count); + } + delete iter; + + return frameCount; +} + +// frameProtocolLength() returns the sum of all the individual protocol sizes +// which may be different from frameLen() +int StreamBase::frameProtocolLength(int frameIndex) const +{ + int len = 0; + ProtocolListIterator *iter = createProtocolListIterator(); + + while (iter->hasNext()) + { + AbstractProtocol *proto = iter->next(); + + len += proto->protocolFrameSize(frameIndex); + } + delete iter; + + return len; +} + +int StreamBase::frameCount() const +{ + int count = 0; + + switch (sendUnit()) + { + case e_su_packets: count = numPackets(); break; + case e_su_bursts: count = numBursts() * burstSize(); break; + default: Q_ASSERT(false); // unreachable + } + + return count; +} + +int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const +{ + int pktLen, len = 0; + + pktLen = frameLen(frameIndex); + + // pktLen is adjusted for CRC/FCS which will be added by the NIC + pktLen -= kFcsSize; + + if ((pktLen < 0) || (pktLen > bufMaxSize)) + return 0; + + ProtocolListIterator *iter; + + iter = createProtocolListIterator(); + while (iter->hasNext()) + { + AbstractProtocol *proto; + QByteArray ba; + + proto = iter->next(); + ba = proto->protocolFrameValue(frameIndex); + + if (len + ba.size() < bufMaxSize) + memcpy(buf+len, ba.constData(), ba.size()); + len += ba.size(); + } + delete iter; + + // Pad with zero, if required + if (len < pktLen) + memset(buf+len, 0, pktLen-len); + + return pktLen; +} + +bool StreamBase::preflightCheck(QString &result) const +{ + bool pass = true; + int count = isFrameSizeVariable() ? frameCount() : 1; + + for (int i = 0; i < count; i++) + { + if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) + { + result += QString("One or more frames may be truncated - " + "frame length should be at least %1.\n") + .arg(frameProtocolLength(i) + kFcsSize); + pass = false; + } + + if (frameLen(i) > 1522) + { + result += QString("Jumbo frames may be truncated or dropped " + "if not supported by the hardware\n"); + pass = false; + } + } + + return pass; +} + +bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) +{ + return stream1->ordinal() < stream2->ordinal() ? true : false; +} diff --git a/common/streambase.h b/common/streambase.h new file mode 100644 index 0000000..9ef3ba1 --- /dev/null +++ b/common/streambase.h @@ -0,0 +1,150 @@ +/* +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 +*/ + +#ifndef _STREAM_BASE_H +#define _STREAM_BASE_H + +#include +#include + +#include "protocol.pb.h" + +const int kFcsSize = 4; + +class AbstractProtocol; +class ProtocolList; +class ProtocolListIterator; + +class StreamBase +{ +private: + OstProto::StreamId *mStreamId; + OstProto::StreamCore *mCore; + OstProto::StreamControl *mControl; + + ProtocolList *currentFrameProtocols; + +public: + StreamBase(); + ~StreamBase(); + + void protoDataCopyFrom(const OstProto::Stream &stream); + void protoDataCopyInto(OstProto::Stream &stream) const; + + ProtocolListIterator* createProtocolListIterator() const; + + //! \todo (LOW) should we have a copy constructor?? + +public: + enum FrameLengthMode { + e_fl_fixed, + e_fl_inc, + e_fl_dec, + e_fl_random + }; + + enum SendUnit { + e_su_packets, + e_su_bursts + }; + + enum SendMode { + e_sm_fixed, + e_sm_continuous + }; + + enum NextWhat { + e_nw_stop, + e_nw_goto_next, + e_nw_goto_id + }; + + quint32 id(); + bool setId(quint32 id); + +#if 0 // FIXME(HI): needed? + quint32 portId() + { return mCore->port_id();} + bool setPortId(quint32 id) + { mCore->set_port_id(id); return true;} +#endif + + quint32 ordinal(); + bool setOrdinal(quint32 ordinal); + + bool isEnabled() const; + bool setEnabled(bool flag); + + const QString name() const ; + bool setName(QString name) ; + + // Frame Length (includes FCS); + FrameLengthMode lenMode() const; + bool setLenMode(FrameLengthMode lenMode); + + quint16 frameLen(int streamIndex = 0) const; + bool setFrameLen(quint16 frameLen); + + quint16 frameLenMin() const; + bool setFrameLenMin(quint16 frameLenMin); + + quint16 frameLenMax() const; + bool setFrameLenMax(quint16 frameLenMax); + + quint16 frameLenAvg() const; + + SendUnit sendUnit() const; + bool setSendUnit(SendUnit sendUnit); + + SendMode sendMode() const; + bool setSendMode(SendMode sendMode); + + NextWhat nextWhat() const; + bool setNextWhat(NextWhat nextWhat); + + quint32 numPackets() const; + bool setNumPackets(quint32 numPackets); + + quint32 numBursts() const; + bool setNumBursts(quint32 numBursts); + + quint32 burstSize() const; + bool setBurstSize(quint32 packetsPerBurst); + + double packetRate() const; + bool setPacketRate(double packetsPerSec); + + double burstRate() const; + bool setBurstRate(double burstsPerSec); + + double averagePacketRate() const; + bool setAveragePacketRate(double packetsPerSec); + + bool isFrameVariable() const; + bool isFrameSizeVariable() const; + int frameVariableCount() const; + int frameProtocolLength(int frameIndex) const; + int frameCount() const; + int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; + bool preflightCheck(QString &result) const; + + static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); +}; + +#endif diff --git a/common/svlan.cpp b/common/svlan.cpp new file mode 100644 index 0000000..9b5f65b --- /dev/null +++ b/common/svlan.cpp @@ -0,0 +1,66 @@ +/* +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 +*/ + +#include "svlan.h" +#include "svlan.pb.h" + +SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : VlanProtocol(stream, parent) +{ + data.set_tpid(0x88a8); + data.set_is_override_tpid(true); +} + +SVlanProtocol::~SVlanProtocol() +{ +} + +AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new SVlanProtocol(stream, parent); +} + +quint32 SVlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kSvlanFieldNumber; +} + +void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::svlan)) + data.MergeFrom(protocol.GetExtension(OstProto::svlan)); +} + +QString SVlanProtocol::name() const +{ + return QString("SVlan"); +} + +QString SVlanProtocol::shortName() const +{ + return QString("SVlan"); +} diff --git a/common/svlan.h b/common/svlan.h new file mode 100644 index 0000000..7ba051b --- /dev/null +++ b/common/svlan.h @@ -0,0 +1,42 @@ +/* +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 +*/ + +#ifndef _SVLAN_H +#define _SVLAN_H + +#include "vlan.h" + +class SVlanProtocol : public VlanProtocol +{ +public: + SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~SVlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; +}; + +#endif diff --git a/common/svlan.proto b/common/svlan.proto new file mode 100644 index 0000000..937a9c1 --- /dev/null +++ b/common/svlan.proto @@ -0,0 +1,27 @@ +/* +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 +*/ + +import "protocol.proto"; +import "vlan.proto"; + +package OstProto; + +extend Protocol { + optional Vlan svlan = 204; +} diff --git a/common/svlanconfig.h b/common/svlanconfig.h new file mode 100644 index 0000000..e5bd578 --- /dev/null +++ b/common/svlanconfig.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SVLAN_CONFIG_H +#define _SVLAN_CONFIG_H + +#include "vlanconfig.h" + +typedef VlanConfigForm SVlanConfigForm; + +#endif + diff --git a/common/svlanpdml.cpp b/common/svlanpdml.cpp new file mode 100644 index 0000000..113f515 --- /dev/null +++ b/common/svlanpdml.cpp @@ -0,0 +1,110 @@ +/* +Copyright (C) 2011 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 "svlanpdml.h" + +#include "eth2.pb.h" +#include "svlan.pb.h" + +PdmlSvlanProtocol::PdmlSvlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; +} + +PdmlProtocol* PdmlSvlanProtocol::createInstance() +{ + return new PdmlSvlanProtocol(); +} + +void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes svlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the svlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kSvlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) + { + bool isOk; + OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ad.cvid") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kSvlanFieldNumber); + + OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); + + svlan->set_tpid(0x88a8); + svlan->set_is_override_tpid(true); + + bool isOk; + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + svlan->set_vlan_tag(tag); + } + else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/svlanpdml.h b/common/svlanpdml.h new file mode 100644 index 0000000..517cc0f --- /dev/null +++ b/common/svlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SVLAN_PDML_H +#define _SVLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlSvlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlSvlanProtocol(); +}; + +#endif diff --git a/common/tcp.cpp b/common/tcp.cpp new file mode 100644 index 0000000..2f9a5b4 --- /dev/null +++ b/common/tcp.cpp @@ -0,0 +1,606 @@ +/* +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 +*/ + +#include "tcp.h" + + +TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +TcpProtocol::~TcpProtocol() +{ +} + +AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TcpProtocol(stream, parent); +} + +quint32 TcpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTcpFieldNumber; +} + +void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::tcp)) + data.MergeFrom(protocol.GetExtension(OstProto::tcp)); +} + +QString TcpProtocol::name() const +{ + return QString("Transmission Control Protocol"); +} + +QString TcpProtocol::shortName() const +{ + return QString("TCP"); +} + +AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 TcpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x06; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int TcpProtocol::fieldCount() const +{ + return tcp_fieldCount; +} + +AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case tcp_src_port: + case tcp_dst_port: + case tcp_seq_num: + case tcp_ack_num: + case tcp_hdrlen: + case tcp_rsvd: + case tcp_flags: + case tcp_window: + break; + + case tcp_cksum: + flags |= CksumField; + break; + + case tcp_urg_ptr: + break; + + case tcp_is_override_src_port: + case tcp_is_override_dst_port: + case tcp_is_override_hdrlen: + case tcp_is_override_cksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case tcp_src_port: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_dst_port: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case tcp_seq_num: + switch(attrib) + { + case FieldName: + return QString("Sequence Number"); + case FieldValue: + return data.seq_num(); + case FieldTextValue: + return QString("%1").arg(data.seq_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_ack_num: + switch(attrib) + { + case FieldName: + return QString("Acknowledgement Number"); + case FieldValue: + return data.ack_num(); + case FieldTextValue: + return QString("%1").arg(data.ack_num()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(4); + qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_hdrlen: + switch(attrib) + { + case FieldName: + return QString("Header Length"); + case FieldValue: + if (data.is_override_hdrlen()) + return ((data.hdrlen_rsvd() >> 4) & 0x0F); + else + return 5; + case FieldTextValue: + if (data.is_override_hdrlen()) + return QString("%1 bytes").arg( + 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QString("20 bytes"); + case FieldFrameValue: + if (data.is_override_hdrlen()) + return QByteArray(1, + (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); + else + return QByteArray(1, (char) 0x05); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_rsvd: + switch(attrib) + { + case FieldName: + return QString("Reserved"); + case FieldValue: + return (data.hdrlen_rsvd() & 0x0F); + case FieldTextValue: + return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); + case FieldFrameValue: + return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); + case FieldBitSize: + return 4; + default: + break; + } + break; + + case tcp_flags: + switch(attrib) + { + case FieldName: + return QString("Flags"); + case FieldValue: + return (data.flags()); + case FieldTextValue: + { + QString s; + s.append("URG: "); + s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); + s.append(" ACK: "); + s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); + s.append(" PSH: "); + s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); + s.append(" RST: "); + s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); + s.append(" SYN: "); + s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); + s.append(" FIN: "); + s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); + return s; + } + case FieldFrameValue: + return QByteArray(1, (char)(data.flags() & 0x3F)); + default: + break; + } + break; + + case tcp_window: + switch(attrib) + { + case FieldName: + return QString("Window Size"); + case FieldValue: + return data.window(); + case FieldTextValue: + return QString("%1").arg(data.window()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.window(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + case tcp_cksum: + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return cksum; + } + case FieldTextValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); + } + case FieldFrameValue: + { + quint16 cksum; + + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + + QByteArray fv; + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 16; + default: + break; + } + break; + + case tcp_urg_ptr: + switch(attrib) + { + case FieldName: + return QString("Urgent Pointer"); + case FieldValue: + return data.urg_ptr(); + case FieldTextValue: + return QString("%1").arg(data.urg_ptr()); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + + // Meta fields + case tcp_is_override_src_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case tcp_is_override_dst_port: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case tcp_is_override_hdrlen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_hdrlen(); + default: + break; + } + break; + } + case tcp_is_override_cksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TcpProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case tcp_src_port: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case tcp_dst_port: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case tcp_seq_num: + { + uint seqNum = value.toUInt(&isOk); + if (isOk) + data.set_seq_num(seqNum); + break; + } + case tcp_ack_num: + { + uint ackNum = value.toUInt(&isOk); + if (isOk) + data.set_ack_num(ackNum); + break; + } + case tcp_hdrlen: + { + uint hdrLen = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); + break; + } + case tcp_rsvd: + { + uint rsvd = value.toUInt(&isOk); + if (isOk) + data.set_hdrlen_rsvd( + (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); + break; + } + case tcp_flags: + { + uint flags = value.toUInt(&isOk); + if (isOk) + data.set_flags(flags); + break; + } + case tcp_window: + { + uint window = value.toUInt(&isOk); + if (isOk) + data.set_window(window); + break; + } + case tcp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + case tcp_urg_ptr: + { + uint urgPtr = value.toUInt(&isOk); + if (isOk) + data.set_urg_ptr(urgPtr); + break; + } + case tcp_is_override_src_port: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_dst_port: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_hdrlen: + { + data.set_is_override_hdrlen(value.toBool()); + isOk = true; + break; + } + case tcp_is_override_cksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool TcpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int TcpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} + diff --git a/common/tcp.h b/common/tcp.h new file mode 100644 index 0000000..276ba65 --- /dev/null +++ b/common/tcp.h @@ -0,0 +1,89 @@ +/* +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 +*/ + +#ifndef _TCP_H +#define _TCP_H + +#include "abstractprotocol.h" + +#include "tcp.pb.h" + +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_FIN 0x01 + +class TcpProtocol : public AbstractProtocol +{ +public: + enum tcpfield + { + tcp_src_port = 0, + tcp_dst_port, + tcp_seq_num, + tcp_ack_num, + tcp_hdrlen, + tcp_rsvd, + tcp_flags, + tcp_window, + tcp_cksum, + tcp_urg_ptr, + + tcp_is_override_src_port, + tcp_is_override_dst_port, + tcp_is_override_hdrlen, + tcp_is_override_cksum, + + tcp_fieldCount + }; + + TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TcpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Tcp data; +}; + +#endif diff --git a/common/tcp.proto b/common/tcp.proto new file mode 100644 index 0000000..93bd762 --- /dev/null +++ b/common/tcp.proto @@ -0,0 +1,47 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +// Tcp +message Tcp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_hdrlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + + optional uint32 seq_num = 7 [default = 129018]; + optional uint32 ack_num = 8; + + optional uint32 hdrlen_rsvd = 9 [default = 0x50]; + optional uint32 flags = 10; + + optional uint32 window = 11 [default = 1024]; + optional uint32 cksum = 12; + optional uint32 urg_ptr = 13; +} + +extend Protocol { + optional Tcp tcp = 400; +} + diff --git a/common/tcp.ui b/common/tcp.ui new file mode 100644 index 0000000..6f3eee8 --- /dev/null +++ b/common/tcp.ui @@ -0,0 +1,268 @@ + + tcp + + + + 0 + 0 + 447 + 194 + + + + Form + + + + + + Override Source Port + + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Urgent Pointer + + + + + + + + + + Sequence Number + + + + + + + + + + Flags + + + + + + URG + + + + + + + ACK + + + + + + + PSH + + + + + + + RST + + + + + + + SYN + + + + + + + FIN + + + + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + Acknowledgement Number + + + + + + + + + + Override Header Length (x4) + + + + + + + false + + + + + + + Window + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbTcpHdrLenOverride + toggled(bool) + leTcpHdrLen + setEnabled(bool) + + + 141 + 123 + + + 187 + 123 + + + + + cbTcpCksumOverride + toggled(bool) + leTcpCksum + setEnabled(bool) + + + 316 + 14 + + + 384 + 17 + + + + + cbTcpSrcPortOverride + toggled(bool) + leTcpSrcPort + setEnabled(bool) + + + 159 + 16 + + + 178 + 18 + + + + + cbTcpDstPortOverride + toggled(bool) + leTcpDstPort + setEnabled(bool) + + + 147 + 45 + + + 180 + 44 + + + + + diff --git a/common/tcpconfig.cpp b/common/tcpconfig.cpp new file mode 100644 index 0000000..ff08f7d --- /dev/null +++ b/common/tcpconfig.cpp @@ -0,0 +1,175 @@ +/* +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 +*/ + +#include "tcpconfig.h" +#include "tcp.h" + +TcpConfigForm::TcpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +TcpConfigForm::~TcpConfigForm() +{ +} + +TcpConfigForm* TcpConfigForm::createInstance() +{ + return new TcpConfigForm; +} + +void TcpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leTcpSrcPort->setText( + proto->fieldData( + TcpProtocol::tcp_src_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpSrcPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_src_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpDstPort->setText( + proto->fieldData( + TcpProtocol::tcp_dst_port, + AbstractProtocol::FieldValue + ).toString()); + cbTcpDstPortOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_dst_port, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpSeqNum->setText( + proto->fieldData( + TcpProtocol::tcp_seq_num, + AbstractProtocol::FieldValue + ).toString()); + leTcpAckNum->setText( + proto->fieldData( + TcpProtocol::tcp_ack_num, + AbstractProtocol::FieldValue + ).toString()); + + leTcpHdrLen->setText( + proto->fieldData( + TcpProtocol::tcp_hdrlen, + AbstractProtocol::FieldValue + ).toString()); + cbTcpHdrLenOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_hdrlen, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpWindow->setText( + proto->fieldData( + TcpProtocol::tcp_window, + AbstractProtocol::FieldValue + ).toString()); + + leTcpCksum->setText(uintToHexStr( + proto->fieldData( + TcpProtocol::tcp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbTcpCksumOverride->setChecked( + proto->fieldData( + TcpProtocol::tcp_is_override_cksum, + AbstractProtocol::FieldValue + ).toBool()); + + leTcpUrgentPointer->setText( + proto->fieldData( + TcpProtocol::tcp_urg_ptr, + AbstractProtocol::FieldValue + ).toString()); + + uint flags = proto->fieldData( + TcpProtocol::tcp_flags, + AbstractProtocol::FieldValue + ).toUInt(); + + cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); + cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); + cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); + cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); + cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); + cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); +} + +void TcpConfigForm::storeWidget(AbstractProtocol *proto) +{ + int ff = 0; + + proto->setFieldData( + TcpProtocol::tcp_src_port, + leTcpSrcPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_src_port, + cbTcpSrcPortOverride->isChecked()); + proto->setFieldData( + TcpProtocol::tcp_dst_port, + leTcpDstPort->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_dst_port, + cbTcpDstPortOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_seq_num, + leTcpSeqNum->text()); + proto->setFieldData( + TcpProtocol::tcp_ack_num, + leTcpAckNum->text()); + + proto->setFieldData( + TcpProtocol::tcp_hdrlen, + leTcpHdrLen->text()); + proto->setFieldData( + TcpProtocol::tcp_is_override_hdrlen, + cbTcpHdrLenOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_window, + leTcpWindow->text()); + + proto->setFieldData( + TcpProtocol::tcp_cksum, + hexStrToUInt(leTcpCksum->text())); + proto->setFieldData( + TcpProtocol::tcp_is_override_cksum, + cbTcpCksumOverride->isChecked()); + + proto->setFieldData( + TcpProtocol::tcp_urg_ptr, + leTcpUrgentPointer->text()); + + if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; + if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; + if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; + if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; + if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; + if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; + + proto->setFieldData(TcpProtocol::tcp_flags, ff); +} + diff --git a/common/tcpconfig.h b/common/tcpconfig.h new file mode 100644 index 0000000..859238a --- /dev/null +++ b/common/tcpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TCP_CONFIG_H +#define _TCP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_tcp.h" + +class TcpConfigForm : + public AbstractProtocolConfigForm, + private Ui::tcp +{ + Q_OBJECT +public: + TcpConfigForm(QWidget *parent = 0); + virtual ~TcpConfigForm(); + + static TcpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/tcppdml.cpp b/common/tcppdml.cpp new file mode 100644 index 0000000..3980b6a --- /dev/null +++ b/common/tcppdml.cpp @@ -0,0 +1,98 @@ +/* +Copyright (C) 2011 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 "tcppdml.h" + +#include "hexdump.pb.h" +#include "tcp.pb.h" + +PdmlTcpProtocol::PdmlTcpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; + + fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); + fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); + fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); + fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); + fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); + fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); + fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); + fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); + fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); +} + +PdmlProtocol* PdmlTcpProtocol::createInstance() +{ + return new PdmlTcpProtocol(); +} + +void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) +{ + if (name == "tcp.options") + options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + else if (name == "") + { + if (attributes.value("show").toString().startsWith("Acknowledgement number")) + { + bool isOk; + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); + } +#if 0 + else if (attributes.value("show").toString().startsWith("TCP segment data")) + { + segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); + stream->mutable_core()->mutable_name()->insert(0, + segmentData_.constData(), segmentData_.size()); + } +#endif + } +} + +void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); + + qDebug("Tcp: post\n"); + + tcp->set_is_override_src_port(true); + tcp->set_is_override_dst_port(true); + tcp->set_is_override_hdrlen(true); + tcp->set_is_override_cksum(true); + + if (options_.size()) + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kHexDumpFieldNumber); + + OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); + + hexDump->mutable_content()->append(options_.constData(), + options_.size()); + hexDump->set_pad_until_end(false); + options_.resize(0); + } +} + diff --git a/common/tcppdml.h b/common/tcppdml.h new file mode 100644 index 0000000..5c3d1c6 --- /dev/null +++ b/common/tcppdml.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TCP_PDML_H +#define _TCP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTcpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTcpProtocol(); +private: + QByteArray options_; + QByteArray segmentData_; +}; + +#endif diff --git a/common/textproto.cpp b/common/textproto.cpp new file mode 100644 index 0000000..285668d --- /dev/null +++ b/common/textproto.cpp @@ -0,0 +1,240 @@ +/* +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 +*/ + +#include "textproto.h" + +TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +TextProtocol::~TextProtocol() +{ +} + +AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new TextProtocol(stream, parent); +} + +quint32 TextProtocol::protocolNumber() const +{ + return OstProto::Protocol::kTextProtocolFieldNumber; +} + +void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::textProtocol)) + data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); +} + +QString TextProtocol::name() const +{ + return QString("Text Protocol"); +} + +QString TextProtocol::shortName() const +{ + return QString("TEXT"); +} + +quint32 TextProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdTcpUdp: return data.port_num(); + default:break; + } + + return AbstractProtocol::protocolId(type); +} + +int TextProtocol::fieldCount() const +{ + return textProto_fieldCount; +} + +AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case textProto_text: + break; + + case textProto_portNum: + case textProto_eol: + case textProto_encoding: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case textProto_text: + { + switch(attrib) + { + case FieldName: + return QString("Text"); + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.text()); + case FieldFrameValue: + { + QString text; + Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); + text = QString().fromStdString(data.text()); + + if (data.eol() == OstProto::TextProtocol::kCrLf) + text.replace('\n', "\r\n"); + else if (data.eol() == OstProto::TextProtocol::kCr) + text.replace('\n', '\r'); + + return text.toUtf8(); + } + default: + break; + } + break; + + } + + // Meta fields + case textProto_portNum: + { + switch(attrib) + { + case FieldValue: + return data.port_num(); + default: + break; + } + break; + } + case textProto_eol: + { + switch(attrib) + { + case FieldValue: + return data.eol(); + default: + break; + } + break; + } + case textProto_encoding: + { + switch(attrib) + { + case FieldValue: + return data.encoding(); + default: + break; + } + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool TextProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case textProto_text: + { + data.set_text(value.toString().toUtf8()); + isOk = true; + break; + } + case textProto_portNum: + { + uint portNum = value.toUInt(&isOk); + if (isOk) + data.set_port_num(portNum); + break; + } + case textProto_eol: + { + uint eol = value.toUInt(&isOk); + if (isOk && data.EndOfLine_IsValid(eol)) + data.set_eol((OstProto::TextProtocol::EndOfLine) eol); + else + isOk = false; + break; + } + case textProto_encoding: + { + uint enc = value.toUInt(&isOk); + if (isOk && data.TextEncoding_IsValid(enc)) + data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); + else + isOk = false; + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int TextProtocol::protocolFrameSize(int streamIndex) const +{ + return fieldData(textProto_text, FieldFrameValue, streamIndex) + .toByteArray().size() ; +} diff --git a/common/textproto.h b/common/textproto.h new file mode 100644 index 0000000..8c00e47 --- /dev/null +++ b/common/textproto.h @@ -0,0 +1,77 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_H +#define _TEXT_PROTOCOL_H + +#include "abstractprotocol.h" +#include "textproto.pb.h" + +/* +TextProtocol Protocol Frame Format - + specified text with the specified line ending and encoded with the + specified encoding +*/ + +class TextProtocol : public AbstractProtocol +{ +public: + enum textProtocolField + { + // Frame Fields + textProto_text = 0, + + // Meta Fields + textProto_portNum, + textProto_eol, + textProto_encoding, + + textProto_fieldCount + }; + + TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~TextProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + +private: + OstProto::TextProtocol data; +}; + +#endif diff --git a/common/textproto.proto b/common/textproto.proto new file mode 100644 index 0000000..e20e496 --- /dev/null +++ b/common/textproto.proto @@ -0,0 +1,44 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Any Text based protocol +message TextProtocol { + enum TextEncoding { + kUtf8 = 0; + } + + enum EndOfLine { + kCr = 0; + kLf = 1; + kCrLf = 2; + } + + optional uint32 port_num = 1 [default = 80]; + optional TextEncoding encoding = 2 [default = kUtf8]; + optional string text = 3; + optional EndOfLine eol = 4 [default = kLf]; +} + +extend Protocol { + optional TextProtocol textProtocol = 500; +} diff --git a/common/textproto.ui b/common/textproto.ui new file mode 100644 index 0000000..6e7ebdb --- /dev/null +++ b/common/textproto.ui @@ -0,0 +1,108 @@ + + TextProto + + + + 0 + 0 + 535 + 300 + + + + Form + + + + + + TCP/UDP Port Number (Protocol) + + + portNumCombo + + + + + + + + 2 + 0 + + + + + + + + Line Ending + + + + + + + 2 + + + + CR + + + + + LF + + + + + CRLF + + + + + + + + Encode as + + + encodingCombo + + + + + + + + 1 + 0 + + + + + UTF-8 + + + + + + + + false + + + + + + + + IntComboBox + QComboBox +
    intcombobox.h
    +
    +
    + + +
    diff --git a/common/textprotoconfig.cpp b/common/textprotoconfig.cpp new file mode 100644 index 0000000..0242ffa --- /dev/null +++ b/common/textprotoconfig.cpp @@ -0,0 +1,84 @@ +/* +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 +*/ + +#include "textprotoconfig.h" +#include "textproto.h" + +TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); + portNumCombo->addItem(0, "Reserved"); + portNumCombo->addItem(80, "HTTP"); + portNumCombo->addItem(554, "RTSP"); + portNumCombo->addItem(5060, "SIP"); +} + +TextProtocolConfigForm::~TextProtocolConfigForm() +{ +} + +TextProtocolConfigForm* TextProtocolConfigForm::createInstance() +{ + return new TextProtocolConfigForm; +} + +void TextProtocolConfigForm::loadWidget(AbstractProtocol *proto) +{ + portNumCombo->setValue( + proto->fieldData( + TextProtocol::textProto_portNum, + AbstractProtocol::FieldValue + ).toUInt()); + eolCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_eol, + AbstractProtocol::FieldValue + ).toUInt()); + encodingCombo->setCurrentIndex( + proto->fieldData( + TextProtocol::textProto_encoding, + AbstractProtocol::FieldValue + ).toUInt()); + protoText->setText( + proto->fieldData( + TextProtocol::textProto_text, + AbstractProtocol::FieldValue + ).toString()); +} + +void TextProtocolConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + TextProtocol::textProto_portNum, + portNumCombo->currentValue()); + proto->setFieldData( + TextProtocol::textProto_eol, + eolCombo->currentIndex()); + proto->setFieldData( + TextProtocol::textProto_encoding, + encodingCombo->currentIndex()); + + proto->setFieldData( + TextProtocol::textProto_text, + protoText->toPlainText()); +} + diff --git a/common/textprotoconfig.h b/common/textprotoconfig.h new file mode 100644 index 0000000..a1971ab --- /dev/null +++ b/common/textprotoconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _TEXT_PROTOCOL_CONFIG_H +#define _TEXT_PROTOCOL_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_textproto.h" + +class TextProtocolConfigForm : + public AbstractProtocolConfigForm, + private Ui::TextProto +{ + Q_OBJECT +public: + TextProtocolConfigForm(QWidget *parent = 0); + virtual ~TextProtocolConfigForm(); + + static TextProtocolConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/textprotopdml.cpp b/common/textprotopdml.cpp new file mode 100644 index 0000000..26ccfe7 --- /dev/null +++ b/common/textprotopdml.cpp @@ -0,0 +1,171 @@ +/* +Copyright (C) 2011 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 "textprotopdml.h" + +#include "textproto.pb.h" + +PdmlTextProtocol::PdmlTextProtocol() +{ + ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; +} + +PdmlProtocol* PdmlTextProtocol::createInstance() +{ + return new PdmlTextProtocol(); +} + +void PdmlTextProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + bool isOk; + int size; + int pos = attributes.value("pos").toString().toUInt(&isOk); + + if (!isOk) + { + if (expectedPos >= 0) + expPos_ = pos = expectedPos; + else + goto _skip_pos_size_proc; + } + + size = attributes.value("size").toString().toUInt(&isOk); + if (!isOk) + goto _skip_pos_size_proc; + + // If pos+size goes beyond the frame length, this is a "reassembled" + // protocol and should be skipped + if ((pos + size) > int(stream->core().frame_len())) + goto _skip_pos_size_proc; + + expPos_ = pos; + endPos_ = expPos_ + size; + +_skip_pos_size_proc: + qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + text->set_port_num(0); + text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF + + detectEol_ = true; + contentType_ = kUnknownContent; +} + +void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ +_retry: + switch(contentType_) + { + case kUnknownContent: + if (name == "data") + contentType_ = kOtherContent; + else + contentType_ = kTextContent; + goto _retry; + break; + + case kTextContent: + { + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + if ((name == "data") + || (attributes.value("show") == "HTTP chunked response")) + { + contentType_ = kOtherContent; + goto _retry; + } + + if (pos < expPos_) + break; + + if ((pos + size) > endPos_) + break; + + if (pos > expPos_) + { + int gap = pos - expPos_; + QByteArray filler(gap, '\n'); + + if (text->eol() == OstProto::TextProtocol::kCrLf) + { + if (gap & 0x01) // Odd + { + filler.resize(gap/2 + 1); + filler[0]=int(' '); + } + else // Even + filler.resize(gap/2); + } + + text->mutable_text()->append(filler.constData(), filler.size()); + expPos_ += gap; + } + + QByteArray line = QByteArray::fromHex( + attributes.value("value").toString().toUtf8()); + + if (detectEol_) + { + if (line.right(2) == "\r\n") + text->set_eol(OstProto::TextProtocol::kCrLf); + else if (line.right(1) == "\r") + text->set_eol(OstProto::TextProtocol::kCr); + else if (line.right(1) == "\n") + text->set_eol(OstProto::TextProtocol::kLf); + + detectEol_ = false; + } + + // Convert line endings to LF only - Qt reqmt that TextProto honours + line.replace("\r\n", "\n"); + line.replace('\r', '\n'); + + text->mutable_text()->append(line.constData(), line.size()); + expPos_ += size; + break; + } + case kOtherContent: + // Do nothing! + break; + default: + Q_ASSERT(false); + } +} + +void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream) +{ + OstProto::TextProtocol *text = pbProto->MutableExtension( + OstProto::textProtocol); + + // Empty Text Content - remove ourselves + if (text->text().length() == 0) + stream->mutable_protocol()->RemoveLast(); + + expPos_ = endPos_ = -1; + detectEol_ = true; + contentType_ = kUnknownContent; +} diff --git a/common/textprotopdml.h b/common/textprotopdml.h new file mode 100644 index 0000000..daaca0c --- /dev/null +++ b/common/textprotopdml.h @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _TEXT_PROTO_PDML_H +#define _TEXT_PROTO_PDML_H + +#include "pdmlprotocol.h" + +class PdmlTextProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlTextProtocol(); +private: + enum ContentType { + kUnknownContent, + kTextContent, + kOtherContent + }; + + bool detectEol_; + ContentType contentType_; + int expPos_; + int endPos_; +}; + +#endif diff --git a/common/udp.cpp b/common/udp.cpp new file mode 100644 index 0000000..f3d051c --- /dev/null +++ b/common/udp.cpp @@ -0,0 +1,431 @@ +/* +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 +*/ + +#include "udp.h" + +UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +UdpProtocol::~UdpProtocol() +{ +} + +AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UdpProtocol(stream, parent); +} + +quint32 UdpProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUdpFieldNumber; +} + +void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::udp)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::udp)) + data.MergeFrom(protocol.GetExtension(OstProto::udp)); +} + +QString UdpProtocol::name() const +{ + return QString("User Datagram Protocol"); +} + +QString UdpProtocol::shortName() const +{ + return QString("UDP"); +} + +AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const +{ + return ProtocolIdTcpUdp; +} + +quint32 UdpProtocol::protocolId(ProtocolIdType type) const +{ + switch(type) + { + case ProtocolIdIp: return 0x11; + default: break; + } + + return AbstractProtocol::protocolId(type); +} + +int UdpProtocol::fieldCount() const +{ + return udp_fieldCount; +} + +AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case udp_srcPort: + case udp_dstPort: + case udp_totLen: + break; + + case udp_cksum: + flags |= CksumField; + break; + + case udp_isOverrideSrcPort: + case udp_isOverrideDstPort: + case udp_isOverrideTotLen: + case udp_isOverrideCksum: + flags &= ~FrameField; + flags |= MetaField; + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return flags; +} + +QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case udp_srcPort: + { + quint16 srcPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_src_port()) + srcPort = data.src_port(); + else + srcPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + srcPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Source Port"); + case FieldValue: + return srcPort; + case FieldTextValue: + return QString("%1").arg(srcPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(srcPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_dstPort: + { + quint16 dstPort; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + if (data.is_override_dst_port()) + dstPort = data.dst_port(); + else + dstPort = payloadProtocolId(ProtocolIdTcpUdp); + break; + default: + dstPort = 0; // avoid the 'maybe used unitialized' warning + break; + } + switch(attrib) + { + case FieldName: + return QString("Destination Port"); + case FieldValue: + return dstPort; + case FieldTextValue: + return QString("%1").arg(dstPort); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(dstPort, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + case udp_totLen: + { + + switch(attrib) + { + case FieldName: + return QString("Datagram Length"); + case FieldValue: + { + int totlen; + + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return totlen; + } + case FieldFrameValue: + { + QByteArray fv; + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + fv.resize(2); + qToBigEndian((quint16) totlen, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + { + int totlen; + totlen = data.is_override_totlen() ? + data.totlen() : + (protocolFramePayloadSize(streamIndex) + 8); + return QString("%1").arg(totlen); + } + case FieldBitSize: + return 16; + default: + break; + } + break; + } + case udp_cksum: + { + quint16 cksum; + + switch(attrib) + { + case FieldValue: + case FieldFrameValue: + case FieldTextValue: + { + if (data.is_override_cksum()) + cksum = data.cksum(); + else + cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); + qDebug("UDP cksum = %hu", cksum); + break; + } + default: + cksum = 0; + break; + } + + switch(attrib) + { + case FieldName: + return QString("Checksum"); + case FieldValue: + return cksum; + case FieldFrameValue: + { + QByteArray fv; + + fv.resize(2); + qToBigEndian(cksum, (uchar*) fv.data()); + return fv; + } + case FieldTextValue: + return QString("0x%1"). + arg(cksum, 4, BASE_HEX, QChar('0'));; + case FieldBitSize: + return 16; + default: + break; + } + break; + } + + // Meta fields + case udp_isOverrideSrcPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_src_port(); + default: + break; + } + break; + } + case udp_isOverrideDstPort: + { + switch(attrib) + { + case FieldValue: + return data.is_override_dst_port(); + default: + break; + } + break; + } + case udp_isOverrideTotLen: + { + switch(attrib) + { + case FieldValue: + return data.is_override_totlen(); + default: + break; + } + break; + } + case udp_isOverrideCksum: + { + switch(attrib) + { + case FieldValue: + return data.is_override_cksum(); + default: + break; + } + break; + } + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UdpProtocol::setFieldData(int index, const QVariant& value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case udp_isOverrideSrcPort: + { + data.set_is_override_src_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideDstPort: + { + data.set_is_override_dst_port(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideTotLen: + { + data.set_is_override_totlen(value.toBool()); + isOk = true; + break; + } + case udp_isOverrideCksum: + { + data.set_is_override_cksum(value.toBool()); + isOk = true; + break; + } + case udp_srcPort: + { + uint srcPort = value.toUInt(&isOk); + if (isOk) + data.set_src_port(srcPort); + break; + } + case udp_dstPort: + { + uint dstPort = value.toUInt(&isOk); + if (isOk) + data.set_dst_port(dstPort); + break; + } + case udp_totLen: + { + uint totLen = value.toUInt(&isOk); + if (isOk) + data.set_totlen(totLen); + break; + } + case udp_cksum: + { + uint cksum = value.toUInt(&isOk); + if (isOk) + data.set_cksum(cksum); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +bool UdpProtocol::isProtocolFrameValueVariable() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return false; + else + return isProtocolFramePayloadValueVariable(); +} + +int UdpProtocol::protocolFrameVariableCount() const +{ + if (data.is_override_totlen() && data.is_override_cksum()) + return 1; + + return protocolFramePayloadVariableCount(); +} diff --git a/common/udp.h b/common/udp.h new file mode 100644 index 0000000..56285b4 --- /dev/null +++ b/common/udp.h @@ -0,0 +1,75 @@ +/* +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 +*/ + +#ifndef _UDP_H +#define _UDP_H + +#include "abstractprotocol.h" +#include "udp.pb.h" + +class UdpProtocol : public AbstractProtocol +{ +public: + enum udpfield + { + udp_srcPort = 0, + udp_dstPort, + udp_totLen, + udp_cksum, + + udp_isOverrideSrcPort, + udp_isOverrideDstPort, + udp_isOverrideTotLen, + udp_isOverrideCksum, + + udp_fieldCount + }; + + UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UdpProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual ProtocolIdType protocolIdType() const; + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual bool isProtocolFrameValueVariable() const; + virtual int protocolFrameVariableCount() const; + +private: + OstProto::Udp data; +}; + +#endif diff --git a/common/udp.proto b/common/udp.proto new file mode 100644 index 0000000..802135e --- /dev/null +++ b/common/udp.proto @@ -0,0 +1,39 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// UDP +message Udp { + optional bool is_override_src_port = 1; + optional bool is_override_dst_port = 2; + optional bool is_override_totlen = 3; + optional bool is_override_cksum = 4; + + optional uint32 src_port = 5 [default = 49152]; + optional uint32 dst_port = 6 [default = 49153]; + optional uint32 totlen = 7; + optional uint32 cksum = 8; +} + +extend Protocol { + optional Udp udp = 401; +} diff --git a/common/udp.ui b/common/udp.ui new file mode 100644 index 0000000..ab979e9 --- /dev/null +++ b/common/udp.ui @@ -0,0 +1,174 @@ + + udp + + + + 0 + 0 + 246 + 144 + + + + Form + + + + + + + + Override Source Port + + + + + + + false + + + + + + + Override Destination Port + + + + + + + false + + + + + + + Override Length + + + + + + + false + + + + + + + Override Checksum + + + + + + + false + + + >HH HH; + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + cbUdpLengthOverride + toggled(bool) + leUdpLength + setEnabled(bool) + + + 59 + 63 + + + 209 + 81 + + + + + cbUdpCksumOverride + toggled(bool) + leUdpCksum + setEnabled(bool) + + + 55 + 106 + + + 209 + 107 + + + + + cbUdpDstPortOverride + toggled(bool) + leUdpDstPort + setEnabled(bool) + + + 131 + 43 + + + 166 + 46 + + + + + cbUdpSrcPortOverride + toggled(bool) + leUdpSrcPort + setEnabled(bool) + + + 125 + 21 + + + 167 + 20 + + + + + diff --git a/common/udpconfig.cpp b/common/udpconfig.cpp new file mode 100644 index 0000000..dab05ec --- /dev/null +++ b/common/udpconfig.cpp @@ -0,0 +1,114 @@ +/* +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 +*/ + +#include "udpconfig.h" +#include "udp.h" + +UdpConfigForm::UdpConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +UdpConfigForm::~UdpConfigForm() +{ +} + +UdpConfigForm* UdpConfigForm::createInstance() +{ + return new UdpConfigForm; +} + + +void UdpConfigForm::loadWidget(AbstractProtocol *proto) +{ + leUdpSrcPort->setText( + proto->fieldData( + UdpProtocol::udp_srcPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpSrcPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideSrcPort, + AbstractProtocol::FieldValue + ).toBool()); + leUdpDstPort->setText( + proto->fieldData( + UdpProtocol::udp_dstPort, + AbstractProtocol::FieldValue + ).toString()); + cbUdpDstPortOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideDstPort, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpLength->setText( + proto->fieldData( + UdpProtocol::udp_totLen, + AbstractProtocol::FieldValue + ).toString()); + cbUdpLengthOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideTotLen, + AbstractProtocol::FieldValue + ).toBool()); + + leUdpCksum->setText(uintToHexStr( + proto->fieldData( + UdpProtocol::udp_cksum, + AbstractProtocol::FieldValue + ).toUInt(), 2)); + cbUdpCksumOverride->setChecked( + proto->fieldData( + UdpProtocol::udp_isOverrideCksum, + AbstractProtocol::FieldValue + ).toBool()); +} + +void UdpConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UdpProtocol::udp_srcPort, + leUdpSrcPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideSrcPort, + cbUdpSrcPortOverride->isChecked()); + proto->setFieldData( + UdpProtocol::udp_dstPort, + leUdpDstPort->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideDstPort, + cbUdpDstPortOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_totLen, + leUdpLength->text()); + proto->setFieldData( + UdpProtocol::udp_isOverrideTotLen, + cbUdpLengthOverride->isChecked()); + + proto->setFieldData( + UdpProtocol::udp_cksum, + hexStrToUInt(leUdpCksum->text())); + proto->setFieldData( + UdpProtocol::udp_isOverrideCksum, + cbUdpCksumOverride->isChecked()); +} + diff --git a/common/udpconfig.h b/common/udpconfig.h new file mode 100644 index 0000000..98b8ecb --- /dev/null +++ b/common/udpconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _UDP_CONFIG_H +#define _UDP_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_udp.h" + +class UdpConfigForm : + public AbstractProtocolConfigForm, + private Ui::udp +{ + Q_OBJECT +public: + UdpConfigForm(QWidget *parent = 0); + virtual ~UdpConfigForm(); + + static UdpConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/udppdml.cpp b/common/udppdml.cpp new file mode 100644 index 0000000..0cb1685 --- /dev/null +++ b/common/udppdml.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 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 "udppdml.h" + +#include "udp.pb.h" + +PdmlUdpProtocol::PdmlUdpProtocol() +{ + ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; + + fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); + fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); + fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum_coverage", + OstProto::Udp::kTotlenFieldNumber); + fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); +} + +PdmlProtocol* PdmlUdpProtocol::createInstance() +{ + return new PdmlUdpProtocol(); +} + +void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream* /*stream*/) +{ + OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); + + qDebug("Udp: post\n"); + + udp->set_is_override_src_port(true); + udp->set_is_override_dst_port(true); + udp->set_is_override_totlen(true); + udp->set_is_override_cksum(true); +} + diff --git a/common/udppdml.h b/common/udppdml.h new file mode 100644 index 0000000..3ae1d6e --- /dev/null +++ b/common/udppdml.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _UDP_PDML_H +#define _UDP_PDML_H + +#include "pdmlprotocol.h" + +class PdmlUdpProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + virtual void postProtocolHandler(OstProto::Protocol *pbProto, + OstProto::Stream *stream); +protected: + PdmlUdpProtocol(); +}; + +#endif diff --git a/common/userscript.cpp b/common/userscript.cpp new file mode 100644 index 0000000..f00ab6c --- /dev/null +++ b/common/userscript.cpp @@ -0,0 +1,555 @@ +/* +Copyright (C) 2010, 2014 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 "userscript.h" + +// +// -------------------- UserScriptProtocol -------------------- +// + +UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent), + userProtocol_(this) +{ + isScriptValid_ = false; + errorLineNumber_ = 0; + + userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); + engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); + + QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); + engine_.globalObject().setProperty("Protocol", meta); +} + +UserScriptProtocol::~UserScriptProtocol() +{ +} + +AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new UserScriptProtocol(stream, parent); +} + +quint32 UserScriptProtocol::protocolNumber() const +{ + return OstProto::Protocol::kUserScriptFieldNumber; +} + +void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::userScript)) + data.MergeFrom(protocol.GetExtension(OstProto::userScript)); + + evaluateUserScript(); +} + +QString UserScriptProtocol::name() const +{ + return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +QString UserScriptProtocol::shortName() const +{ + return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); +} + +quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolId"); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, type)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolId(type); +} + +int UserScriptProtocol::fieldCount() const +{ + return userScript_fieldCount; +} + +QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case userScript_program: + + switch(attrib) + { + case FieldName: + return QString("UserProtocol"); + + case FieldValue: + case FieldTextValue: + return QString().fromStdString(data.program()); + + case FieldFrameValue: + { + if (!isScriptValid_) + return QByteArray(); + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameValue"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isArray()); + + QByteArray fv; + QList pktBuf; + + qScriptValueToSequence(userValue, pktBuf); + + fv.resize(pktBuf.size()); + for (int i = 0; i < pktBuf.size(); i++) + fv[i] = pktBuf.at(i) & 0xFF; + + return fv; + } + default: + break; + } + break; + + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool UserScriptProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case userScript_program: + { + data.set_program(value.toString().toStdString()); + evaluateUserScript(); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} + +int UserScriptProtocol::protocolFrameSize(int streamIndex) const +{ + if (!isScriptValid_) + return 0; + + QScriptValue userFunction = userProtocolScriptValue_.property( + "protocolFrameSize"); + + Q_ASSERT(userFunction.isValid()); + Q_ASSERT(userFunction.isFunction()); + + QScriptValue userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex)); + + Q_ASSERT(userValue.isNumber()); + + return userValue.toInt32(); +} + +bool UserScriptProtocol::isProtocolFrameValueVariable() const +{ + return userProtocol_.isProtocolFrameValueVariable(); +} + +bool UserScriptProtocol::isProtocolFrameSizeVariable() const +{ + return userProtocol_.isProtocolFrameSizeVariable(); +} + +int UserScriptProtocol::protocolFrameVariableCount() const +{ + return userProtocol_.protocolFrameVariableCount(); +} + +quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, + CksumType cksumType) const +{ + QScriptValue userFunction; + QScriptValue userValue; + + if (!isScriptValid_) + goto _do_default; + + userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); + + qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _do_default; + + Q_ASSERT(userFunction.isFunction()); + + userValue = userFunction.call(QScriptValue(), + QScriptValueList() << QScriptValue(&engine_, streamIndex) + << QScriptValue(&engine_, cksumType)); + + Q_ASSERT(userValue.isValid()); + Q_ASSERT(userValue.isNumber()); + + return userValue.toUInt32(); + +_do_default: + return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); +} + +void UserScriptProtocol::evaluateUserScript() const +{ + QScriptValue userFunction; + QScriptValue userValue; + QString property; + + isScriptValid_ = false; + errorLineNumber_ = userScriptLineCount(); + + // Reset all properties including the dynamic ones + userProtocol_.reset(); + userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); + userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); + + engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); + if (engine_.hasUncaughtException()) + goto _error_exception; + + // Validate protocolFrameValue() + property = QString("protocolFrameValue"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isArray:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isArray()); + + if (!userValue.isArray()) + { + errorText_ = property + QString(" does not return an array"); + goto _error_exit; + } + + // Validate protocolFrameSize() + property = QString("protocolFrameSize"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + { + errorText_ = property + QString(" not set"); + goto _error_exit; + } + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + // Validate protocolFrameCksum() [optional] + property = QString("protocolFrameCksum"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_cksum; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_cksum: + // Validate protocolId() [optional] + property = QString("protocolId"); + userFunction = userProtocolScriptValue_.property(property); + + qDebug("userscript property %s: isValid:%d/isFunc:%d", + property.toAscii().constData(), + userFunction.isValid(), userFunction.isFunction()); + + if (!userFunction.isValid()) + goto _skip_protocol_id; + + if (!userFunction.isFunction()) + { + errorText_ = property + QString(" is not a function"); + goto _error_exit; + } + + userValue = userFunction.call(); + if (engine_.hasUncaughtException()) + goto _error_exception; + + qDebug("userscript property %s return value: isValid:%d/isNumber:%d", + property.toAscii().constData(), + userValue.isValid(), userValue.isNumber()); + + if (!userValue.isNumber()) + { + errorText_ = property + QString(" does not return a number"); + goto _error_exit; + } + + +_skip_protocol_id: + errorText_ = QString(""); + isScriptValid_ = true; + return; + +_error_exception: + errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); + errorText_ = engine_.uncaughtException().toString(); + +_error_exit: + userProtocol_.reset(); + return; +} + +bool UserScriptProtocol::isScriptValid() const +{ + return isScriptValid_; +} + +int UserScriptProtocol::userScriptErrorLineNumber() const +{ + return errorLineNumber_; +} + +QString UserScriptProtocol::userScriptErrorText() const +{ + return errorText_; +} + +int UserScriptProtocol::userScriptLineCount() const +{ + return fieldData(userScript_program, FieldValue).toString().count( + QChar('\n')) + 1; +} + +// +// -------------------- UserProtocol -------------------- +// + +UserProtocol::UserProtocol(AbstractProtocol *parent) + : parent_ (parent) +{ + reset(); +} + +void UserProtocol::reset() +{ + name_ = QString(); + protocolFrameValueVariable_ = false; + protocolFrameSizeVariable_ = false; + protocolFrameVariableCount_ = 1; +} + +QString UserProtocol::name() const +{ + return name_; +} + +void UserProtocol::setName(QString &name) +{ + name_ = name; +} + +bool UserProtocol::isProtocolFrameValueVariable() const +{ + return protocolFrameValueVariable_; +} + +void UserProtocol::setProtocolFrameValueVariable(bool variable) +{ + protocolFrameValueVariable_ = variable; +} + +bool UserProtocol::isProtocolFrameSizeVariable() const +{ + return protocolFrameSizeVariable_; +} + +void UserProtocol::setProtocolFrameSizeVariable(bool variable) +{ + protocolFrameSizeVariable_ = variable; +} + +int UserProtocol::protocolFrameVariableCount() const +{ + return protocolFrameVariableCount_; +} + +void UserProtocol::setProtocolFrameVariableCount(int count) +{ + protocolFrameVariableCount_ = count; +} + +quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const +{ + return parent_->payloadProtocolId( + static_cast(type)); +} + +int UserProtocol::protocolFrameOffset(int streamIndex) const +{ + return parent_->protocolFrameOffset(streamIndex); +} + +int UserProtocol::protocolFramePayloadSize(int streamIndex) const +{ + return parent_->protocolFramePayloadSize(streamIndex); +} + +bool UserProtocol::isProtocolFramePayloadValueVariable() const +{ + return parent_->isProtocolFramePayloadValueVariable(); +} + +bool UserProtocol::isProtocolFramePayloadSizeVariable() const +{ + return parent_->isProtocolFramePayloadSizeVariable(); +} + +int UserProtocol::protocolFramePayloadVariableCount() const +{ + return parent_->protocolFramePayloadVariableCount(); +} + +quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); +} + +quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, + AbstractProtocol::CksumType cksumType) const +{ + quint32 cksum; + + cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); + qDebug("UserProto:%s = %d", __FUNCTION__, cksum); + return cksum; +} + +/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ diff --git a/common/userscript.h b/common/userscript.h new file mode 100644 index 0000000..f6bf6ef --- /dev/null +++ b/common/userscript.h @@ -0,0 +1,162 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _USER_SCRIPT_H +#define _USER_SCRIPT_H + +#include "abstractprotocol.h" +#include "userscript.pb.h" + +#include +#include + +class UserScriptProtocol; + +class UserProtocol : public QObject +{ + Q_OBJECT; + Q_ENUMS(ProtocolIdType); + Q_ENUMS(CksumType); + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(bool protocolFrameValueVariable + READ isProtocolFrameValueVariable + WRITE setProtocolFrameValueVariable); + Q_PROPERTY(bool protocolFrameSizeVariable + READ isProtocolFrameSizeVariable + WRITE setProtocolFrameSizeVariable); + Q_PROPERTY(int protocolFrameVariableCount + READ protocolFrameVariableCount + WRITE setProtocolFrameVariableCount); + +public: + enum ProtocolIdType + { + ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, + ProtocolIdEth = AbstractProtocol::ProtocolIdEth, + ProtocolIdIp = AbstractProtocol::ProtocolIdIp, + ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp + }; + + enum CksumType + { + CksumIp = AbstractProtocol::CksumIp, + CksumIpPseudo = AbstractProtocol::CksumIpPseudo, + CksumTcpUdp = AbstractProtocol::CksumTcpUdp + }; + + UserProtocol(AbstractProtocol *parent); + +public slots: + void reset(); + + QString name() const; + void setName(QString &name); + + bool isProtocolFrameValueVariable() const; + void setProtocolFrameValueVariable(bool variable); + bool isProtocolFrameSizeVariable() const; + void setProtocolFrameSizeVariable(bool variable); + int protocolFrameVariableCount() const; + void setProtocolFrameVariableCount(int count); + + quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; + int protocolFrameOffset(int streamIndex = 0) const; + int protocolFramePayloadSize(int streamIndex = 0) const; + + bool isProtocolFramePayloadValueVariable() const; + bool isProtocolFramePayloadSizeVariable() const; + int protocolFramePayloadVariableCount() const; + + quint32 protocolFrameHeaderCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + quint32 protocolFramePayloadCksum(int streamIndex = 0, + AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; + +private: + AbstractProtocol *parent_; + + QString name_; + bool protocolFrameValueVariable_; + bool protocolFrameSizeVariable_; + int protocolFrameVariableCount_; +}; + +class UserScriptProtocol : public AbstractProtocol +{ +public: + enum userScriptfield + { + // Frame Fields + userScript_program = 0, + + userScript_fieldCount + }; + + UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~UserScriptProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual quint32 protocolId(ProtocolIdType type) const; + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + + virtual int protocolFrameSize(int streamIndex = 0) const; + + virtual bool isProtocolFrameValueVariable() const; + virtual bool isProtocolFrameSizeVariable() const; + virtual int protocolFrameVariableCount() const; + + virtual quint32 protocolFrameCksum(int streamIndex = 0, + CksumType cksumType = CksumIp) const; + + void evaluateUserScript() const; + bool isScriptValid() const; + int userScriptErrorLineNumber() const; + QString userScriptErrorText() const; + +private: + int userScriptLineCount() const; + + OstProto::UserScript data; + + mutable QScriptEngine engine_; + mutable UserProtocol userProtocol_; + mutable QScriptValue userProtocolScriptValue_; + + mutable bool isScriptValid_; + mutable int errorLineNumber_; + mutable QString errorText_; +}; + +#endif diff --git a/common/userscript.proto b/common/userscript.proto new file mode 100644 index 0000000..aa1e195 --- /dev/null +++ b/common/userscript.proto @@ -0,0 +1,33 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Sample Protocol +message UserScript { + + optional string program = 1; + +} + +extend Protocol { + optional UserScript userScript = 103; +} diff --git a/common/userscript.ui b/common/userscript.ui new file mode 100644 index 0000000..e18e024 --- /dev/null +++ b/common/userscript.ui @@ -0,0 +1,70 @@ + + UserScript + + + + 0 + 0 + 517 + 335 + + + + Form + + + + + + + + + + 10 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Unknown + + + + + + + + + + Compile + + + + + + + + diff --git a/common/userscriptconfig.cpp b/common/userscriptconfig.cpp new file mode 100644 index 0000000..85e0aba --- /dev/null +++ b/common/userscriptconfig.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2010, 2014 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 "userscriptconfig.h" +#include "userscript.h" + +#include + +UserScriptConfigForm::UserScriptConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); + + // The protocol_ (UserScriptProtocol) is a dummy protocol internal + // to UserScriptConfigForm whose sole purpose is to "compile" the script + // so that the configForm widget can display the compilation result. + // It is *not* used for actual packet contents at any time + protocol_ = new UserScriptProtocol(NULL); + compileScript(); +} + +UserScriptConfigForm::~UserScriptConfigForm() +{ + delete protocol_; +} + +UserScriptConfigForm* UserScriptConfigForm::createInstance() +{ + return new UserScriptConfigForm; +} + +void UserScriptConfigForm::loadWidget(AbstractProtocol *proto) +{ + programEdit->setPlainText( + proto->fieldData( + UserScriptProtocol::userScript_program, + AbstractProtocol::FieldValue + ).toString()); + + compileScript(); +} + +void UserScriptConfigForm::storeWidget(AbstractProtocol *proto) +{ + proto->setFieldData( + UserScriptProtocol::userScript_program, + programEdit->toPlainText()); +} + +// +// ----- private methods +// +void UserScriptConfigForm::compileScript() +{ + // storeWidget() will save the updated userscript into + // the protocol_ which in turn triggers the protocol_ to + // compile it + storeWidget(protocol_); + if (protocol_->isScriptValid()) + { + statusLabel->setText(QString("Success")); + compileButton->setDisabled(true); + } + else + { + statusLabel->setText( + QString("Error: %1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + compileButton->setEnabled(true); + } +} + +// +// ----- private slots +// +void UserScriptConfigForm::on_programEdit_textChanged() +{ + compileButton->setEnabled(true); +} + +void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) +{ + compileScript(); + if (!protocol_->isScriptValid()) + { + QMessageBox::critical(this, "Error", + QString("%1: %2").arg( + protocol_->userScriptErrorLineNumber()).arg( + protocol_->userScriptErrorText())); + } +} + diff --git a/common/userscriptconfig.h b/common/userscriptconfig.h new file mode 100644 index 0000000..8285103 --- /dev/null +++ b/common/userscriptconfig.h @@ -0,0 +1,51 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _USER_SCRIPT_CONFIG_H +#define _USER_SCRIPT_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_userscript.h" + +class UserScriptProtocol; + +class UserScriptConfigForm : + public AbstractProtocolConfigForm, + private Ui::UserScript +{ + Q_OBJECT + +public: + UserScriptConfigForm(QWidget *parent = 0); + virtual ~UserScriptConfigForm(); + + static UserScriptConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); + +private: + void compileScript(); + UserScriptProtocol *protocol_; + +private slots: + void on_programEdit_textChanged(); + void on_compileButton_clicked(bool checked = false); +}; +#endif diff --git a/common/vlan.cpp b/common/vlan.cpp new file mode 100644 index 0000000..cb07cd2 --- /dev/null +++ b/common/vlan.cpp @@ -0,0 +1,270 @@ +/* +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 +*/ + +#include "vlan.h" + +VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) + : AbstractProtocol(stream, parent) +{ +} + +VlanProtocol::~VlanProtocol() +{ +} + +AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, + AbstractProtocol *parent) +{ + return new VlanProtocol(stream, parent); +} + +quint32 VlanProtocol::protocolNumber() const +{ + return OstProto::Protocol::kVlanFieldNumber; +} + +void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const +{ + protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); + protocol.mutable_protocol_id()->set_id(protocolNumber()); +} + +void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) +{ + if (protocol.protocol_id().id() == protocolNumber() && + protocol.HasExtension(OstProto::vlan)) + data.MergeFrom(protocol.GetExtension(OstProto::vlan)); +} + +QString VlanProtocol::name() const +{ + return QString("Vlan"); +} + +QString VlanProtocol::shortName() const +{ + return QString("Vlan"); +} + +int VlanProtocol::fieldCount() const +{ + return vlan_fieldCount; +} + +AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const +{ + AbstractProtocol::FieldFlags flags; + + flags = AbstractProtocol::fieldFlags(index); + + switch (index) + { + case vlan_tpid: + case vlan_prio: + case vlan_cfiDei: + case vlan_vlanId: + break; + + // meta-fields + case vlan_isOverrideTpid: + flags &= ~FrameField; + flags |= MetaField; + break; + } + + return flags; +} + +QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, + int streamIndex) const +{ + switch (index) + { + case vlan_tpid: + { + quint16 tpid; + + tpid = data.is_override_tpid() ? data.tpid() : 0x8100; + + switch(attrib) + { + case FieldName: + return QString("Tag Protocol Id"); + case FieldValue: + return tpid; + case FieldTextValue: + return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian(tpid, (uchar*) fv.data()); + return fv; + } + default: + break; + } + break; + } + + case vlan_prio: + { + uint prio = ((data.vlan_tag() >> 13) & 0x07); + + switch(attrib) + { + case FieldName: + return QString("Priority"); + case FieldValue: + return prio; + case FieldTextValue: + return QString("%1").arg(prio); + case FieldFrameValue: + return QByteArray(1, (char) prio); + case FieldBitSize: + return 3; + default: + break; + } + break; + } + + case vlan_cfiDei: + { + uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); + + switch(attrib) + { + case FieldName: + return QString("CFI/DEI"); + case FieldValue: + return cfiDei; + case FieldTextValue: + return QString("%1").arg(cfiDei); + case FieldFrameValue: + return QByteArray(1, (char) cfiDei); + case FieldBitSize: + return 1; + default: + break; + } + break; + } + + case vlan_vlanId: + { + quint16 vlanId = (data.vlan_tag() & 0x0FFF); + + switch(attrib) + { + case FieldName: + return QString("VLAN Id"); + case FieldValue: + return vlanId; + case FieldTextValue: + return QString("%1").arg(vlanId); + case FieldFrameValue: + { + QByteArray fv; + fv.resize(2); + qToBigEndian((quint16) vlanId, (uchar*) fv.data()); + return fv; + } + case FieldBitSize: + return 12; + default: + break; + } + break; + } + // Meta fields + + case vlan_isOverrideTpid: + switch(attrib) + { + case FieldValue: return data.is_override_tpid(); + default: break; + } + break; + default: + break; + } + + return AbstractProtocol::fieldData(index, attrib, streamIndex); +} + +bool VlanProtocol::setFieldData(int index, const QVariant &value, + FieldAttrib attrib) +{ + bool isOk = false; + + if (attrib != FieldValue) + goto _exit; + + switch (index) + { + case vlan_tpid: + { + uint tpid = value.toUInt(&isOk); + if (isOk) + data.set_tpid(tpid); + break; + } + case vlan_prio: + { + uint prio = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((prio & 0x07) << 13) | (data.vlan_tag() & 0x1FFF)); + break; + } + case vlan_cfiDei: + { + uint cfiDei = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + ((cfiDei & 0x01) << 12) | (data.vlan_tag() & 0xEFFF)); + break; + } + case vlan_vlanId: + { + uint vlanId = value.toUInt(&isOk); + if (isOk) + data.set_vlan_tag( + (vlanId & 0x0FFF) | (data.vlan_tag() & 0xF000)); + break; + } + + // Meta-Fields + case vlan_isOverrideTpid: + { + bool override = value.toUInt(&isOk); + if (isOk) + data.set_is_override_tpid(override); + break; + } + default: + qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, + index); + break; + } + +_exit: + return isOk; +} diff --git a/common/vlan.h b/common/vlan.h new file mode 100644 index 0000000..26cf874 --- /dev/null +++ b/common/vlan.h @@ -0,0 +1,67 @@ +/* +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 +*/ + +#ifndef _VLAN_H +#define _VLAN_H + +#include "abstractprotocol.h" +#include "vlan.pb.h" + +class VlanProtocol : public AbstractProtocol +{ +public: + enum Vlanfield + { + vlan_tpid, + vlan_prio, + vlan_cfiDei, + vlan_vlanId, + + // meta-fields + vlan_isOverrideTpid, + + vlan_fieldCount + }; + + VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); + virtual ~VlanProtocol(); + + static AbstractProtocol* createInstance(StreamBase *stream, + AbstractProtocol *parent = 0); + virtual quint32 protocolNumber() const; + + virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; + virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); + + virtual QString name() const; + virtual QString shortName() const; + + virtual int fieldCount() const; + + virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; + virtual QVariant fieldData(int index, FieldAttrib attrib, + int streamIndex = 0) const; + virtual bool setFieldData(int index, const QVariant &value, + FieldAttrib attrib = FieldValue); + +protected: + OstProto::Vlan data; +}; + +#endif diff --git a/common/vlan.proto b/common/vlan.proto new file mode 100644 index 0000000..0bfc2a0 --- /dev/null +++ b/common/vlan.proto @@ -0,0 +1,34 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; +message Vlan { + // VLAN presence/absence + optional bool is_override_tpid = 1; + + // VLAN values + optional uint32 tpid = 2; + optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid +} + +extend Protocol { + optional Vlan vlan = 205; +} diff --git a/common/vlan.ui b/common/vlan.ui new file mode 100644 index 0000000..72e49c0 --- /dev/null +++ b/common/vlan.ui @@ -0,0 +1,181 @@ + + Vlan + + + + 0 + 0 + 274 + 106 + + + + Form + + + + + + true + + + Override TPID + + + + + + + Priority + + + + + + + CFI/DEI + + + + + + + VLAN + + + + + + + false + + + >HH HH; + + + + + + + + + + true + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + + + + true + + + + 0 + + + + + 1 + + + + + + + + true + + + 0 + + + + + + + Qt::Horizontal + + + + 111 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 51 + + + + + + + + + + cbTpidOverride + toggled(bool) + leTpid + setEnabled(bool) + + + 59 + 41 + + + 59 + 57 + + + + + diff --git a/common/vlanconfig.cpp b/common/vlanconfig.cpp new file mode 100644 index 0000000..35241bd --- /dev/null +++ b/common/vlanconfig.cpp @@ -0,0 +1,87 @@ +/* +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 +*/ + +#include "vlanconfig.h" +#include "vlan.h" + +VlanConfigForm::VlanConfigForm(QWidget *parent) + : AbstractProtocolConfigForm(parent) +{ + setupUi(this); +} + +VlanConfigForm::~VlanConfigForm() +{ +} + +VlanConfigForm* VlanConfigForm::createInstance() +{ + return new VlanConfigForm; +} + +void VlanConfigForm::loadWidget(AbstractProtocol *proto) +{ + cbTpidOverride->setChecked( + proto->fieldData( + VlanProtocol::vlan_isOverrideTpid, + AbstractProtocol::FieldValue + ).toBool()); + leTpid->setText(uintToHexStr( + proto->fieldData( + VlanProtocol::vlan_tpid, + AbstractProtocol::FieldValue) + .toUInt(), 2)); + cmbPrio->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_prio, + AbstractProtocol::FieldValue) + .toUInt()); + cmbCfiDei->setCurrentIndex( + proto->fieldData( + VlanProtocol::vlan_cfiDei, + AbstractProtocol::FieldValue) + .toUInt()); + leVlanId->setText( + proto->fieldData( + VlanProtocol::vlan_vlanId, + AbstractProtocol::FieldValue) + .toString()); +} + +void VlanConfigForm::storeWidget(AbstractProtocol *proto) +{ + bool isOk; + + proto->setFieldData( + VlanProtocol::vlan_isOverrideTpid, + cbTpidOverride->isChecked()); + proto->setFieldData( + VlanProtocol::vlan_tpid, + leTpid->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); + proto->setFieldData( + VlanProtocol::vlan_prio, + cmbPrio->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_cfiDei, + cmbCfiDei->currentIndex()); + proto->setFieldData( + VlanProtocol::vlan_vlanId, + leVlanId->text()); +} + diff --git a/common/vlanconfig.h b/common/vlanconfig.h new file mode 100644 index 0000000..a761873 --- /dev/null +++ b/common/vlanconfig.h @@ -0,0 +1,41 @@ +/* +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 +*/ + +#ifndef _VLAN_CONFIG_H +#define _VLAN_CONFIG_H + +#include "abstractprotocolconfig.h" +#include "ui_vlan.h" + +class VlanConfigForm : + public AbstractProtocolConfigForm, + private Ui::Vlan +{ + Q_OBJECT +public: + VlanConfigForm(QWidget *parent = 0); + virtual ~VlanConfigForm(); + + static VlanConfigForm* createInstance(); + + virtual void loadWidget(AbstractProtocol *proto); + virtual void storeWidget(AbstractProtocol *proto); +}; + +#endif diff --git a/common/vlanpdml.cpp b/common/vlanpdml.cpp new file mode 100644 index 0000000..722d038 --- /dev/null +++ b/common/vlanpdml.cpp @@ -0,0 +1,91 @@ +/* +Copyright (C) 2011 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 "vlanpdml.h" + +#include "eth2.pb.h" +#include "vlan.pb.h" + +PdmlVlanProtocol::PdmlVlanProtocol() +{ + ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; +} + +PdmlProtocol* PdmlVlanProtocol::createInstance() +{ + return new PdmlVlanProtocol(); +} + +void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, + const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + + vlan->set_tpid(0x8100); + vlan->set_is_override_tpid(true); + + // If a eth2 protocol precedes vlan, we remove the eth2 protocol + // 'coz the eth2.etherType is actually the vlan.tpid + // + // We assume that the current protocol is the last in the stream + int index = stream->protocol_size() - 1; + if ((index > 1) + && (stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kVlanFieldNumber) + && (stream->protocol(index - 1).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber)) + { + stream->mutable_protocol()->SwapElements(index, index - 1); + Q_ASSERT(stream->protocol(index).protocol_id().id() + == OstProto::Protocol::kEth2FieldNumber); + stream->mutable_protocol()->RemoveLast(); + } +} + +void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, + int /*size*/, const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream) +{ + if (name == "vlan.id") + { + bool isOk; + OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); + uint tag = attributes.value("unmaskedvalue").isEmpty() ? + attributes.value("value").toString().toUInt(&isOk, kBaseHex) : + attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); + + vlan->set_vlan_tag(tag); + } + else if (name == "vlan.etype") + { + OstProto::Protocol *proto = stream->add_protocol(); + + proto->mutable_protocol_id()->set_id( + OstProto::Protocol::kEth2FieldNumber); + + bool isOk; + OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); + + eth2->set_type(attributes.value("value") + .toString().toUInt(&isOk, kBaseHex)); + eth2->set_is_override_type(true); + } +} + diff --git a/common/vlanpdml.h b/common/vlanpdml.h new file mode 100644 index 0000000..6bab024 --- /dev/null +++ b/common/vlanpdml.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _VLAN_PDML_H +#define _VLAN_PDML_H + +#include "pdmlprotocol.h" + +class PdmlVlanProtocol : public PdmlProtocol +{ +public: + static PdmlProtocol* createInstance(); + + virtual void preProtocolHandler(QString name, + const QXmlStreamAttributes &attributes, int expectedPos, + OstProto::Protocol *pbProto, OstProto::Stream *stream); + virtual void unknownFieldHandler(QString name, int pos, int size, + const QXmlStreamAttributes &attributes, + OstProto::Protocol *pbProto, OstProto::Stream *stream); +protected: + PdmlVlanProtocol(); +}; + +#endif diff --git a/common/vlanstack.h b/common/vlanstack.h new file mode 100644 index 0000000..847ac3d --- /dev/null +++ b/common/vlanstack.h @@ -0,0 +1,30 @@ +/* +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 +*/ + +#ifndef _VLAN_STACK_H +#define _VLAN_STACK_H + +#include "comboprotocol.h" +#include "svlan.h" +#include "vlan.h" + +typedef ComboProtocol VlanStackProtocol; + +#endif diff --git a/common/vlanstack.proto b/common/vlanstack.proto new file mode 100644 index 0000000..d6bacd4 --- /dev/null +++ b/common/vlanstack.proto @@ -0,0 +1,31 @@ +/* +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 +*/ + +import "protocol.proto"; + +package OstProto; + +// Stacked VLAN (2 tags) +message VlanStack { + // Empty since this is a 'combo' protocol +} + +extend Protocol { + optional VlanStack vlanStack = 208; +} diff --git a/common/vlanstackconfig.h b/common/vlanstackconfig.h new file mode 100644 index 0000000..5203824 --- /dev/null +++ b/common/vlanstackconfig.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _VLAN_STACK_CONFIG_H +#define _VLAN_STACK_CONFIG_H + +#include "comboprotocolconfig.h" + +#include "svlanconfig.h" +#include "vlanconfig.h" +#include "svlan.h" +#include "vlan.h" + +#include "protocol.pb.h" + +typedef ComboProtocolConfigForm < + OstProto::Protocol::kVlanStackFieldNumber, + SVlanConfigForm, VlanConfigForm, + SVlanProtocol, VlanProtocol + > VlanStackConfigForm; + +#endif diff --git a/extra/extra.pro b/extra/extra.pro new file mode 100644 index 0000000..48aa842 --- /dev/null +++ b/extra/extra.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + qhexedit2 diff --git a/extra/qhexedit2/qhexedit2.pro b/extra/qhexedit2/qhexedit2.pro new file mode 100644 index 0000000..89479e7 --- /dev/null +++ b/extra/qhexedit2/qhexedit2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt staticlib warn_on + +HEADERS = src/commands.h\ + src/qhexedit.h \ + src/qhexedit_p.h \ + src/xbytearray.h + +SOURCES = src/commands.cpp \ + src/qhexedit.cpp \ + src/qhexedit_p.cpp \ + src/xbytearray.cpp diff --git a/extra/qhexedit2/src/commands.cpp b/extra/qhexedit2/src/commands.cpp new file mode 100644 index 0000000..303091d --- /dev/null +++ b/extra/qhexedit2/src/commands.cpp @@ -0,0 +1,115 @@ +#include "commands.h" + +CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _xData = xData; + _charPos = charPos; + _newChar = newChar; + _cmd = cmd; +} + +bool CharCommand::mergeWith(const QUndoCommand *command) +{ + const CharCommand *nextCommand = static_cast(command); + bool result = false; + + if (_cmd != remove) + { + if (nextCommand->_cmd == replace) + if (nextCommand->_charPos == _charPos) + { + _newChar = nextCommand->_newChar; + result = true; + } + } + return result; +} + +void CharCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_charPos, 1); + break; + case replace: + _xData->replace(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + case remove: + _xData->insert(_charPos, _oldChar); + _xData->setDataChanged(_charPos, _wasChanged); + break; + } +} + +void CharCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_charPos, _newChar); + break; + case replace: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->replace(_charPos, _newChar); + break; + case remove: + _oldChar = _xData->data()[_charPos]; + _wasChanged = _xData->dataChanged(_charPos); + _xData->remove(_charPos, 1); + break; + } +} + + + +ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + _cmd = cmd; + _xData = xData; + _baPos = baPos; + _newBa = newBa; + _len = len; +} + +void ArrayCommand::undo() +{ + switch (_cmd) + { + case insert: + _xData->remove(_baPos, _newBa.length()); + break; + case replace: + _xData->replace(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + case remove: + _xData->insert(_baPos, _oldBa); + _xData->setDataChanged(_baPos, _wasChanged); + break; + } +} + +void ArrayCommand::redo() +{ + switch (_cmd) + { + case insert: + _xData->insert(_baPos, _newBa); + break; + case replace: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->replace(_baPos, _newBa); + break; + case remove: + _oldBa = _xData->data().mid(_baPos, _len); + _wasChanged = _xData->dataChanged(_baPos, _len); + _xData->remove(_baPos, _len); + break; + } +} diff --git a/extra/qhexedit2/src/commands.h b/extra/qhexedit2/src/commands.h new file mode 100644 index 0000000..9931b3f --- /dev/null +++ b/extra/qhexedit2/src/commands.h @@ -0,0 +1,70 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +/** \cond docNever */ + +#include + +#include "xbytearray.h" + +/*! CharCommand is a class to prived undo/redo functionality in QHexEdit. +A QUndoCommand represents a single editing action on a document. CharCommand +is responsable for manipulations on single chars. It can insert. replace and +remove characters. A manipulation stores allways to actions +1. redo (or do) action +2. undo action. + +CharCommand also supports command compression via mergeWidht(). This allows +the user to execute a undo command contation e.g. 3 steps in a single command. +If you for example insert a new byt "34" this means for the editor doing 3 +steps: insert a "00", replace it with "03" and the replace it with "34". These +3 steps are combined into a single step, insert a "34". +*/ +class CharCommand : public QUndoCommand +{ +public: + enum { Id = 1234 }; + enum Cmd {insert, remove, replace}; + + CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, + QUndoCommand *parent=0); + + void undo(); + void redo(); + bool mergeWith(const QUndoCommand *command); + int id() const { return Id; } + +private: + XByteArray * _xData; + int _charPos; + bool _wasChanged; + char _newChar; + char _oldChar; + Cmd _cmd; +}; + +/*! ArrayCommand provides undo/redo functionality for handling binary strings. It +can undo/redo insert, replace and remove binary strins (QByteArrays). +*/ +class ArrayCommand : public QUndoCommand +{ +public: + enum Cmd {insert, remove, replace}; + ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, + QUndoCommand *parent=0); + void undo(); + void redo(); + +private: + Cmd _cmd; + XByteArray * _xData; + int _baPos; + int _len; + QByteArray _wasChanged; + QByteArray _newBa; + QByteArray _oldBa; +}; + +/** \endcond docNever */ + +#endif // COMMANDS_H diff --git a/extra/qhexedit2/src/license.txt b/extra/qhexedit2/src/license.txt new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/extra/qhexedit2/src/license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/extra/qhexedit2/src/qhexedit.cpp b/extra/qhexedit2/src/qhexedit.cpp new file mode 100644 index 0000000..b6dd38d --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.cpp @@ -0,0 +1,152 @@ +#include + +#include "qhexedit.h" + + +QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) +{ + qHexEdit_p = new QHexEditPrivate(this); + setWidget(qHexEdit_p); + setWidgetResizable(true); + + connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); + connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); + connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); + setFocusPolicy(Qt::NoFocus); +} + +void QHexEdit::insert(int i, const QByteArray & ba) +{ + qHexEdit_p->insert(i, ba); +} + +void QHexEdit::insert(int i, char ch) +{ + qHexEdit_p->insert(i, ch); +} + +void QHexEdit::remove(int pos, int len) +{ + qHexEdit_p->remove(pos, len); +} + +QString QHexEdit::toReadableString() +{ + return qHexEdit_p->toRedableString(); +} + +QString QHexEdit::selectionToReadableString() +{ + return qHexEdit_p->selectionToReadableString(); +} + +void QHexEdit::setAddressArea(bool addressArea) +{ + qHexEdit_p->setAddressArea(addressArea); +} + +void QHexEdit::redo() +{ + qHexEdit_p->redo(); +} + +void QHexEdit::undo() +{ + qHexEdit_p->undo(); +} + +void QHexEdit::setAddressWidth(int addressWidth) +{ + qHexEdit_p->setAddressWidth(addressWidth); +} + +void QHexEdit::setAsciiArea(bool asciiArea) +{ + qHexEdit_p->setAsciiArea(asciiArea); +} + +void QHexEdit::setHighlighting(bool mode) +{ + qHexEdit_p->setHighlighting(mode); +} + +void QHexEdit::setAddressOffset(int offset) +{ + qHexEdit_p->setAddressOffset(offset); +} + +int QHexEdit::addressOffset() +{ + return qHexEdit_p->addressOffset(); +} + +void QHexEdit::setData(const QByteArray &data) +{ + qHexEdit_p->setData(data); +} + +QByteArray QHexEdit::data() +{ + return qHexEdit_p->data(); +} + +void QHexEdit::setAddressAreaColor(const QColor &color) +{ + qHexEdit_p->setAddressAreaColor(color); +} + +QColor QHexEdit::addressAreaColor() +{ + return qHexEdit_p->addressAreaColor(); +} + +void QHexEdit::setHighlightingColor(const QColor &color) +{ + qHexEdit_p->setHighlightingColor(color); +} + +QColor QHexEdit::highlightingColor() +{ + return qHexEdit_p->highlightingColor(); +} + +void QHexEdit::setSelectionColor(const QColor &color) +{ + qHexEdit_p->setSelectionColor(color); +} + +QColor QHexEdit::selectionColor() +{ + return qHexEdit_p->selectionColor(); +} + +void QHexEdit::setOverwriteMode(bool overwriteMode) +{ + qHexEdit_p->setOverwriteMode(overwriteMode); +} + +bool QHexEdit::overwriteMode() +{ + return qHexEdit_p->overwriteMode(); +} + +void QHexEdit::setReadOnly(bool readOnly) +{ + qHexEdit_p->setReadOnly(readOnly); +} + +bool QHexEdit::isReadOnly() +{ + return qHexEdit_p->isReadOnly(); +} + +void QHexEdit::setFont(const QFont &font) +{ + qHexEdit_p->setFont(font); +} + +const QFont & QHexEdit::font() const +{ + return qHexEdit_p->font(); +} diff --git a/extra/qhexedit2/src/qhexedit.h b/extra/qhexedit2/src/qhexedit.h new file mode 100644 index 0000000..b5a9601 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit.h @@ -0,0 +1,205 @@ +#ifndef QHEXEDIT_H +#define QHEXEDIT_H + +#include +#include "qhexedit_p.h" + +/** \mainpage +QHexEdit is a binary editor widget for Qt. + +\version Version 0.6.1 +\image html hexedit.png +*/ + + +/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework. +It is a simple editor for binary data, just like QPlainTextEdit is for text +data. There are sip configuration files included, so it is easy to create +bindings for PyQt and you can use this widget also in python. + +QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use +the mouse or the keyboard to navigate inside the widget. If you hit the keys +(0..9, a..f) you will change the data. Changed data is highlighted and can be +accessed via data(). + +Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) +and insert data. In this case the size of data() increases. It is also possible +to delete bytes (del or backspace), here the size of data decreases. + +You can select data with keyboard hits or mouse movements. The copy-key will +copy the selected data into the clipboard. The cut-key copies also but delets +it afterwards. In overwrite mode, the paste function overwrites the content of +the (does not change the length) data. In insert mode, clipboard data will be +inserted. The clipboard content is expected in ASCII Hex notation. Unknown +characters will be ignored. + +QHexEdit comes with undo/redo functionality. All changes can be undone, by +pressing the undo-key (usually ctr-z). They can also be redone afterwards. +The undo/redo framework is cleared, when setData() sets up a new +content for the editor. + +This widget can only handle small amounts of data. The size has to be below 10 +megabytes, otherwise the scroll sliders ard not shown and you can't scroll any +more. +*/ + class QHexEdit : public QScrollArea +{ + Q_OBJECT + /*! Property data holds the content of QHexEdit. Call setData() to set the + content of QHexEdit, data() returns the actual content. + */ + Q_PROPERTY(QByteArray data READ data WRITE setData) + + /*! Property addressOffset is added to the Numbers of the Address Area. + A offset in the address area (left side) is sometimes usefull, whe you show + only a segment of a complete memory picture. With setAddressOffset() you set + this property - with addressOffset() you get the actual value. + */ + Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) + + /*! Property address area color sets (setAddressAreaColor()) the backgorund + color of address areas. You can also read the color (addressaAreaColor()). + */ + Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) + + /*! Property highlighting color sets (setHighlightingColor()) the backgorund + color of highlighted text areas. You can also read the color + (highlightingColor()). + */ + Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) + + /*! Property selection color sets (setSelectionColor()) the backgorund + color of selected text areas. You can also read the color + (selectionColor()). + */ + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + + /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode + in which the editor works. In overwrite mode the user will overwrite existing data. The + size of data will be constant. In insert mode the size will grow, when inserting + new data. + */ + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) + + /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode + in which the editor works. In readonly mode the the user can only navigate + through the data and select data; modifying is not possible. This + property's default is false. + */ + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + + /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ + Q_PROPERTY(QFont font READ font WRITE setFont) + + +public: + /*! Creates an instance of QHexEdit. + \param parent Parent widget of QHexEdit. + */ + QHexEdit(QWidget *parent = 0); + + /*! Inserts a byte array. + \param i Index position, where to insert + \param ba byte array, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, const QByteArray & ba); + + /*! Inserts a char. + \param i Index position, where to insert + \param ch Char, which is to insert + In overwrite mode, the existing data will be overwritten, in insertmode ba will be + insertet and size of data grows. + */ + void insert(int i, char ch); + + /*! Removes len bytes from the content. + \param pos Index position, where to remove + \param len Amount of bytes to remove + In overwrite mode, the existing bytes will be overwriten with 0x00. + */ + void remove(int pos, int len=1); + + /*! Gives back a formatted image of the content of QHexEdit + */ + QString toReadableString(); + + /*! Gives back a formatted image of the selected content of QHexEdit + */ + QString selectionToReadableString(); + + /*! \cond docNever */ + void setAddressOffset(int offset); + int addressOffset(); + void setData(QByteArray const &data); + QByteArray data(); + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + void setSelectionColor(QColor const &color); + QColor selectionColor(); + void setOverwriteMode(bool); + bool overwriteMode(); + void setReadOnly(bool); + bool isReadOnly(); + const QFont &font() const; + void setFont(const QFont &); + /*! \endcond docNever */ + +public slots: + /*! Redoes the last operation. If there is no operation to redo, i.e. + there is no redo step in the undo/redo history, nothing happens. + */ + void redo(); + + /*! Set the minimum width of the address area. + \param addressWidth Width in characters. + */ + void setAddressWidth(int addressWidth); + + /*! Switch the address area on or off. + \param addressArea true (show it), false (hide it). + */ + void setAddressArea(bool addressArea); + + /*! Switch the ascii area on or off. + \param asciiArea true (show it), false (hide it). + */ + void setAsciiArea(bool asciiArea); + + /*! Switch the highlighting feature on or of. + \param mode true (show it), false (hide it). + */ + void setHighlighting(bool mode); + + /*! Undoes the last operation. If there is no operation to undo, i.e. + there is no undo step in the undo/redo history, nothing happens. + */ + void undo(); + +signals: + + /*! Contains the address, where the cursor is located. */ + void currentAddressChanged(int address); + + /*! Contains the size of the data to edit. */ + void currentSizeChanged(int size); + + /*! The signal is emited every time, the data is changed. */ + void dataChanged(); + + /*! The signal is emited every time, the overwrite mode is changed. */ + void overwriteModeChanged(bool state); + +private: + /*! \cond docNever */ + QHexEditPrivate *qHexEdit_p; + QHBoxLayout *layout; + QScrollArea *scrollArea; + /*! \endcond docNever */ +}; + +#endif + diff --git a/extra/qhexedit2/src/qhexedit_p.cpp b/extra/qhexedit2/src/qhexedit_p.cpp new file mode 100644 index 0000000..2f046bb --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.cpp @@ -0,0 +1,800 @@ +#include + +#include "qhexedit_p.h" +#include "commands.h" + +const int HEXCHARS_IN_LINE = 47; +const int GAP_ADR_HEX = 10; +const int GAP_HEX_ASCII = 16; +const int BYTES_PER_LINE = 16; + +QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) +{ + _undoStack = new QUndoStack(this); + + _scrollArea = parent; + setAddressWidth(4); + setAddressOffset(0); + setAddressArea(true); + setAsciiArea(true); + setHighlighting(true); + setOverwriteMode(true); + setReadOnly(false); + setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); + setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); + setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); + setFont(QFont("Courier", 10)); + + _size = 0; + resetSelection(0); + + setFocusPolicy(Qt::StrongFocus); + + connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + _cursorTimer.setInterval(500); + _cursorTimer.start(); +} + +void QHexEditPrivate::setAddressOffset(int offset) +{ + _xData.setAddressOffset(offset); + adjust(); +} + +int QHexEditPrivate::addressOffset() +{ + return _xData.addressOffset(); +} + +void QHexEditPrivate::setData(const QByteArray &data) +{ + _xData.setData(data); + _undoStack->clear(); + adjust(); + setCursorPos(0); +} + +QByteArray QHexEditPrivate::data() +{ + return _xData.data(); +} + +void QHexEditPrivate::setAddressAreaColor(const QColor &color) +{ + _addressAreaColor = color; + update(); +} + +QColor QHexEditPrivate::addressAreaColor() +{ + return _addressAreaColor; +} + +void QHexEditPrivate::setHighlightingColor(const QColor &color) +{ + _highlightingColor = color; + update(); +} + +QColor QHexEditPrivate::highlightingColor() +{ + return _highlightingColor; +} + +void QHexEditPrivate::setSelectionColor(const QColor &color) +{ + _selectionColor = color; + update(); +} + +QColor QHexEditPrivate::selectionColor() +{ + return _selectionColor; +} + +void QHexEditPrivate::setReadOnly(bool readOnly) +{ + _readOnly = readOnly; +} + +bool QHexEditPrivate::isReadOnly() +{ + return _readOnly; +} + +XByteArray & QHexEditPrivate::xData() +{ + return _xData; +} + +void QHexEditPrivate::insert(int index, const QByteArray & ba) +{ + if (ba.length() > 0) + { + if (_overwriteMode) + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } +} + +void QHexEditPrivate::insert(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::remove(int index, int len) +{ + if (len > 0) + { + if (len == 1) + { + if (_overwriteMode) + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + else + { + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); + _undoStack->push(charCommand); + emit dataChanged(); + } + } + else + { + QByteArray ba = QByteArray(len, char(0)); + if (_overwriteMode) + { + QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + else + { + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); + _undoStack->push(arrayCommand); + emit dataChanged(); + } + } + } +} + +void QHexEditPrivate::replace(int index, char ch) +{ + QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); + _undoStack->push(charCommand); + emit dataChanged(); +} + +void QHexEditPrivate::replace(int index, const QByteArray & ba) +{ + QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); + _undoStack->push(arrayCommand); + emit dataChanged(); +} + +void QHexEditPrivate::setAddressArea(bool addressArea) +{ + _addressArea = addressArea; + adjust(); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAddressWidth(int addressWidth) +{ + _xData.setAddressWidth(addressWidth); + + setCursorPos(_cursorPosition); +} + +void QHexEditPrivate::setAsciiArea(bool asciiArea) +{ + _asciiArea = asciiArea; + adjust(); +} + +void QHexEditPrivate::setFont(const QFont &font) +{ + QWidget::setFont(font); + adjust(); +} + +void QHexEditPrivate::setHighlighting(bool mode) +{ + _highlighting = mode; + update(); +} + +void QHexEditPrivate::setOverwriteMode(bool overwriteMode) +{ + _overwriteMode = overwriteMode; +} + +bool QHexEditPrivate::overwriteMode() +{ + return _overwriteMode; +} + +void QHexEditPrivate::redo() +{ + _undoStack->redo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +void QHexEditPrivate::undo() +{ + _undoStack->undo(); + emit dataChanged(); + setCursorPos(_cursorPosition); + update(); +} + +QString QHexEditPrivate::toRedableString() +{ + return _xData.toRedableString(); +} + + +QString QHexEditPrivate::selectionToReadableString() +{ + return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); +} + +void QHexEditPrivate::keyPressEvent(QKeyEvent *event) +{ + int charX = (_cursorX - _xPosHex) / _charWidth; + int posX = (charX / 3) * 2 + (charX % 3); + int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; + + +/*****************************************************************************/ +/* Cursor movements */ +/*****************************************************************************/ + + if (event->matches(QKeySequence::MoveToNextChar)) + { + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousChar)) + { + setCursorPos(_cursorPosition - 1); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfLine)) + { + setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfLine)) + { + setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousLine)) + { + setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToNextLine)) + { + setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + + if (event->matches(QKeySequence::MoveToNextPage)) + { + setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToPreviousPage)) + { + setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToEndOfDocument)) + { + setCursorPos(_xData.size() * 2); + resetSelection(_cursorPosition); + } + if (event->matches(QKeySequence::MoveToStartOfDocument)) + { + setCursorPos(0); + resetSelection(_cursorPosition); + } + +/*****************************************************************************/ +/* Select commands */ +/*****************************************************************************/ + if (event->matches(QKeySequence::SelectAll)) + { + resetSelection(0); + setSelection(2*_xData.size() + 1); + } + if (event->matches(QKeySequence::SelectNextChar)) + { + int pos = _cursorPosition + 1; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousChar)) + { + int pos = _cursorPosition - 1; + setSelection(pos); + setCursorPos(pos); + } + if (event->matches(QKeySequence::SelectEndOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfLine)) + { + int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousLine)) + { + int pos = _cursorPosition - (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectNextLine)) + { + int pos = _cursorPosition + (2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + + if (event->matches(QKeySequence::SelectNextPage)) + { + int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectPreviousPage)) + { + int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectEndOfDocument)) + { + int pos = _xData.size() * 2; + setCursorPos(pos); + setSelection(pos); + } + if (event->matches(QKeySequence::SelectStartOfDocument)) + { + int pos = 0; + setCursorPos(pos); + setSelection(pos); + } + +/*****************************************************************************/ +/* Edit Commands */ +/*****************************************************************************/ +if (!_readOnly) +{ + /* Hex input */ + int key = int(event->text()[0].toAscii()); + if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + + // If insert mode, then insert a byte + if (_overwriteMode == false) + if ((charX % 3) == 0) + { + insert(posBa, char(0)); + } + + // Change content + if (_xData.size() > 0) + { + QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); + if ((charX % 3) == 0) + hexValue[0] = key; + else + hexValue[1] = key; + + replace(posBa, QByteArray().fromHex(hexValue)[0]); + + setCursorPos(_cursorPosition + 1); + resetSelection(_cursorPosition); + } + } + + /* Cut & Paste */ + if (event->matches(QKeySequence::Cut)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append("\n"); + } + remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + setCursorPos(getSelectionBegin()); + resetSelection(getSelectionBegin()); + } + + if (event->matches(QKeySequence::Paste)) + { + QClipboard *clipboard = QApplication::clipboard(); + QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); + insert(_cursorPosition / 2, ba); + setCursorPos(_cursorPosition + 2 * ba.length()); + resetSelection(getSelectionBegin()); + } + + + /* Delete char */ + if (event->matches(QKeySequence::Delete)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (_overwriteMode) + replace(posBa, char(0)); + else + remove(posBa, 1); + } + } + + /* Backspace */ + if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) + { + if (getSelectionBegin() != getSelectionEnd()) + { + posBa = getSelectionBegin(); + remove(posBa, getSelectionEnd() - posBa); + setCursorPos(2*posBa); + resetSelection(2*posBa); + } + else + { + if (posBa > 0) + { + if (_overwriteMode) + replace(posBa - 1, char(0)); + else + remove(posBa - 1, 1); + setCursorPos(_cursorPosition - 2); + } + } + } + + /* undo */ + if (event->matches(QKeySequence::Undo)) + { + undo(); + } + + /* redo */ + if (event->matches(QKeySequence::Redo)) + { + redo(); + } + + } + + if (event->matches(QKeySequence::Copy)) + { + QString result = QString(); + for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) + { + result += _xData.data().mid(idx, 1).toHex() + " "; + if ((idx % 16) == 15) + result.append('\n'); + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(result); + } + + // Switch between insert/overwrite mode + if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) + { + _overwriteMode = !_overwriteMode; + setCursorPos(_cursorPosition); + overwriteModeChanged(_overwriteMode); + } + + _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); + update(); +} + +void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int actPos = cursorPos(event->pos()); + setCursorPos(actPos); + setSelection(actPos); +} + +void QHexEditPrivate::mousePressEvent(QMouseEvent * event) +{ + _blink = false; + update(); + int cPos = cursorPos(event->pos()); + resetSelection(cPos); + setCursorPos(cPos); +} + +void QHexEditPrivate::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + // draw some patterns if needed + painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); + if (_addressArea) + painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); + if (_asciiArea) + { + int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); + painter.setPen(Qt::gray); + painter.drawLine(linePos, event->rect().top(), linePos, height()); + } + + painter.setPen(this->palette().color(QPalette::WindowText)); + + // calc position + int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; + if (firstLineIdx < 0) + firstLineIdx = 0; + int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; + if (lastLineIdx > _xData.size()) + lastLineIdx = _xData.size(); + int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; + + // paint address area + if (_addressArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QString address = QString("%1") + .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); + painter.drawText(_xPosAdr, yPos, address); + } + } + + // paint hex area + QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); + QBrush highLighted = QBrush(_highlightingColor); + QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); + QBrush selected = QBrush(_selectionColor); + QPen colSelected = QPen(Qt::white); + QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); + + painter.setBackgroundMode(Qt::TransparentMode); + + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + QByteArray hex; + int xPos = _xPosHex; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + int posBa = lineIdx + colIdx; + if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) + { + painter.setBackground(selected); + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setPen(colSelected); + } + else + { + if (_highlighting) + { + // hilight diff bytes + painter.setBackground(highLighted); + if (_xData.dataChanged(posBa)) + { + painter.setPen(colHighlighted); + painter.setBackgroundMode(Qt::OpaqueMode); + } + else + { + painter.setPen(colStandard); + painter.setBackgroundMode(Qt::TransparentMode); + } + } + } + + // render hex value + if (colIdx == 0) + { + hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); + painter.drawText(xPos, yPos, hex); + xPos += 2 * _charWidth; + } else { + hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); + painter.drawText(xPos, yPos, hex); + xPos += 3 * _charWidth; + } + + } + } + painter.setBackgroundMode(Qt::TransparentMode); + painter.setPen(this->palette().color(QPalette::WindowText)); + + // paint ascii area + if (_asciiArea) + { + for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) + { + int xPosAscii = _xPosAscii; + for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) + { + painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); + xPosAscii += _charWidth; + } + } + } + + // paint cursor + if (_blink) + { + if (_overwriteMode) + painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); + else + painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); + } + + if (_size != _xData.size()) + { + _size = _xData.size(); + emit currentSizeChanged(_size); + } +} + +void QHexEditPrivate::setCursorPos(int position) +{ + // delete cursor + _blink = false; + update(); + + // cursor in range? + if (_overwriteMode) + { + if (position > (_xData.size() * 2 - 1)) + position = _xData.size() * 2 - 1; + } else { + if (position > (_xData.size() * 2)) + position = _xData.size() * 2; + } + + if (position < 0) + position = 0; + + // calc position + _cursorPosition = position; + _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; + int x = (position % (2 * BYTES_PER_LINE)); + _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; + + // immiadately draw cursor + _blink = true; + update(); + emit currentAddressChanged(_cursorPosition/2); +} + +int QHexEditPrivate::cursorPos(QPoint pos) +{ + int result = -1; + // find char under cursor + if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) + { + int x = (pos.x() - _xPosHex) / _charWidth; + if ((x % 3) == 0) + x = (x / 3) * 2; + else + x = ((x / 3) * 2) + 1; + int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; + result = x + y; + } + return result; +} + +int QHexEditPrivate::cursorPos() +{ + return _cursorPosition; +} + +void QHexEditPrivate::resetSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + _selectionInit = pos; + _selectionBegin = pos; + _selectionEnd = pos; +} + +void QHexEditPrivate::setSelection(int pos) +{ + if (pos < 0) + pos = 0; + pos = pos / 2; + if (pos >= _selectionInit) + { + _selectionEnd = pos; + _selectionBegin = _selectionInit; + } + else + { + _selectionBegin = pos; + _selectionEnd = _selectionInit; + } +} + +int QHexEditPrivate::getSelectionBegin() +{ + return _selectionBegin; +} + +int QHexEditPrivate::getSelectionEnd() +{ + return _selectionEnd; +} + + +void QHexEditPrivate::updateCursor() +{ + if (_blink) + _blink = false; + else + _blink = true; + update(_cursorX, _cursorY, _charWidth, _charHeight); +} + +void QHexEditPrivate::adjust() +{ + _charWidth = fontMetrics().width(QLatin1Char('9')); + _charHeight = fontMetrics().height(); + + _xPosAdr = 0; + if (_addressArea) + _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; + else + _xPosHex = 0; + _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; + + // tell QAbstractScollbar, how big we are + setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); + setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); + + update(); +} diff --git a/extra/qhexedit2/src/qhexedit_p.h b/extra/qhexedit2/src/qhexedit_p.h new file mode 100644 index 0000000..b802af3 --- /dev/null +++ b/extra/qhexedit2/src/qhexedit_p.h @@ -0,0 +1,120 @@ +#ifndef QHEXEDIT_P_H +#define QHEXEDIT_P_H + +/** \cond docNever */ + + +#include +#include "xbytearray.h" + +class QHexEditPrivate : public QWidget +{ +Q_OBJECT + +public: + QHexEditPrivate(QScrollArea *parent); + + void setAddressAreaColor(QColor const &color); + QColor addressAreaColor(); + + void setAddressOffset(int offset); + int addressOffset(); + + void setCursorPos(int position); + int cursorPos(); + + void setData(QByteArray const &data); + QByteArray data(); + + void setHighlightingColor(QColor const &color); + QColor highlightingColor(); + + void setOverwriteMode(bool overwriteMode); + bool overwriteMode(); + + void setReadOnly(bool readOnly); + bool isReadOnly(); + + void setSelectionColor(QColor const &color); + QColor selectionColor(); + + XByteArray & xData(); + + void insert(int index, const QByteArray & ba); + void insert(int index, char ch); + void remove(int index, int len=1); + void replace(int index, char ch); + void replace(int index, const QByteArray & ba); + + void setAddressArea(bool addressArea); + void setAddressWidth(int addressWidth); + void setAsciiArea(bool asciiArea); + void setHighlighting(bool mode); + virtual void setFont(const QFont &font); + + void undo(); + void redo(); + + QString toRedableString(); + QString selectionToReadableString(); + +signals: + void currentAddressChanged(int address); + void currentSizeChanged(int size); + void dataChanged(); + void overwriteModeChanged(bool state); + +protected: + void keyPressEvent(QKeyEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mousePressEvent(QMouseEvent * event); + + void paintEvent(QPaintEvent *event); + + int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION + + void resetSelection(int pos); + void setSelection(int pos); // set min (if below init) or max (if greater init) + int getSelectionBegin(); + int getSelectionEnd(); + + +private slots: + void updateCursor(); + +private: + void adjust(); + + QColor _addressAreaColor; + QColor _highlightingColor; + QColor _selectionColor; + QScrollArea *_scrollArea; + QTimer _cursorTimer; + QUndoStack *_undoStack; + + XByteArray _xData; // Hält den Inhalt des Hex Editors + + bool _blink; // true: then cursor blinks + bool _renderingRequired; // Flag to store that rendering is necessary + bool _addressArea; // left area of QHexEdit + bool _asciiArea; // medium area + bool _highlighting; // highlighting of changed bytes + bool _overwriteMode; + bool _readOnly; // true: the user can only look and navigate + + int _charWidth, _charHeight; // char dimensions (dpendend on font) + int _cursorX, _cursorY; // graphics position of the cursor + int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) + int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas + + int _selectionBegin; // First selected char + int _selectionEnd; // Last selected char + int _selectionInit; // That's, where we pressed the mouse button + + int _size; +}; + +/** \endcond docNever */ + +#endif + diff --git a/extra/qhexedit2/src/xbytearray.cpp b/extra/qhexedit2/src/xbytearray.cpp new file mode 100644 index 0000000..ec8bf3d --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.cpp @@ -0,0 +1,167 @@ +#include "xbytearray.h" + +XByteArray::XByteArray() +{ + _oldSize = -99; + _addressNumbers = 4; + _addressOffset = 0; + +} + +int XByteArray::addressOffset() +{ + return _addressOffset; +} + +void XByteArray::setAddressOffset(int offset) +{ + _addressOffset = offset; +} + +int XByteArray::addressWidth() +{ + return _addressNumbers; +} + +void XByteArray::setAddressWidth(int width) +{ + if ((width >= 0) and (width<=6)) + { + _addressNumbers = width; + } +} + +QByteArray & XByteArray::data() +{ + return _data; +} + +void XByteArray::setData(QByteArray data) +{ + _data = data; + _changedData = QByteArray(data.length(), char(0)); +} + +bool XByteArray::dataChanged(int i) +{ + return bool(_changedData[i]); +} + +QByteArray XByteArray::dataChanged(int i, int len) +{ + return _changedData.mid(i, len); +} + +void XByteArray::setDataChanged(int i, bool state) +{ + _changedData[i] = char(state); +} + +void XByteArray::setDataChanged(int i, const QByteArray & state) +{ + int length = state.length(); + int len; + if ((i + length) > _changedData.length()) + len = _changedData.length() - i; + else + len = length; + _changedData.replace(i, len, state); +} + +int XByteArray::realAddressNumbers() +{ + if (_oldSize != _data.size()) + { + // is addressNumbers wide enought? + QString test = QString("%1") + .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); + _realAddressNumbers = test.size(); + } + return _realAddressNumbers; +} + +int XByteArray::size() +{ + return _data.size(); +} + +QByteArray & XByteArray::insert(int i, char ch) +{ + _data.insert(i, ch); + _changedData.insert(i, char(1)); + return _data; +} + +QByteArray & XByteArray::insert(int i, const QByteArray & ba) +{ + _data.insert(i, ba); + _changedData.insert(i, QByteArray(ba.length(), char(1))); + return _data; +} + +QByteArray & XByteArray::remove(int i, int len) +{ + _data.remove(i, len); + _changedData.remove(i, len); + return _data; +} + +QByteArray & XByteArray::replace(int index, char ch) +{ + _data[index] = ch; + _changedData[index] = char(1); + return _data; +} + +QByteArray & XByteArray::replace(int index, const QByteArray & ba) +{ + int len = ba.length(); + return replace(index, len, ba); +} + +QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) +{ + int len; + if ((index + length) > _data.length()) + len = _data.length() - index; + else + len = length; + _data.replace(index, len, ba.mid(0, len)); + _changedData.replace(index, len, QByteArray(len, char(1))); + return _data; +} + +QChar XByteArray::asciiChar(int index) +{ + char ch = _data[index]; + if ((ch < 0x20) or (ch > 0x7e)) + ch = '.'; + return QChar(ch); +} + +QString XByteArray::toRedableString(int start, int end) +{ + int adrWidth = realAddressNumbers(); + if (_addressNumbers > adrWidth) + adrWidth = _addressNumbers; + if (end < 0) + end = _data.size(); + + QString result; + for (int i=start; i < end; i += 16) + { + QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); + QString hexStr; + QString ascStr; + for (int j=0; j<16; j++) + { + if ((i + j) < _data.size()) + { + hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); + ascStr.append(asciiChar(i+j)); + } + } + result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; + } + return result; +} diff --git a/extra/qhexedit2/src/xbytearray.h b/extra/qhexedit2/src/xbytearray.h new file mode 100644 index 0000000..2b67c61 --- /dev/null +++ b/extra/qhexedit2/src/xbytearray.h @@ -0,0 +1,66 @@ +#ifndef XBYTEARRAY_H +#define XBYTEARRAY_H + +/** \cond docNever */ + +#include + +/*! XByteArray represents the content of QHexEcit. +XByteArray comprehend the data itself and informations to store if it was +changed. The QHexEdit component uses these informations to perform nice +rendering of the data + +XByteArray also provides some functionality to insert, replace and remove +single chars and QByteArras. Additionally some functions support rendering +and converting to readable strings. +*/ +class XByteArray +{ +public: + explicit XByteArray(); + + int addressOffset(); + void setAddressOffset(int offset); + + int addressWidth(); + void setAddressWidth(int width); + + QByteArray & data(); + void setData(QByteArray data); + + bool dataChanged(int i); + QByteArray dataChanged(int i, int len); + void setDataChanged(int i, bool state); + void setDataChanged(int i, const QByteArray & state); + + int realAddressNumbers(); + int size(); + + QByteArray & insert(int i, char ch); + QByteArray & insert(int i, const QByteArray & ba); + + QByteArray & remove(int pos, int len); + + QByteArray & replace(int index, char ch); + QByteArray & replace(int index, const QByteArray & ba); + QByteArray & replace(int index, int length, const QByteArray & ba); + + QChar asciiChar(int index); + QString toRedableString(int start=0, int end=-1); + +signals: + +public slots: + +private: + QByteArray _data; + QByteArray _changedData; + + int _addressNumbers; // wanted width of address area + int _addressOffset; // will be added to the real addres inside bytearray + int _realAddressNumbers; // real width of address area (can be greater then wanted width) + int _oldSize; // size of data +}; + +/** \endcond docNever */ +#endif // XBYTEARRAY_H diff --git a/install.pri b/install.pri new file mode 100644 index 0000000..fdb16e0 --- /dev/null +++ b/install.pri @@ -0,0 +1,14 @@ +# A custom install path prefix can be provided by passing PREFIX=/absolute/path +# to qmake; if one is not provided, we use the below defaults - +isEmpty(PREFIX) { + unix:PREFIX = "/usr/local/" + macx:PREFIX = "/Applications/" + win32:PREFIX = "../" +} +macx { + target.path = $$PREFIX/Ostinato +} else { + target.path = $$PREFIX/bin +} + +INSTALLS += target diff --git a/ost.pro b/ost.pro new file mode 100644 index 0000000..0e758eb --- /dev/null +++ b/ost.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = \ + extra \ + rpc/pbrpc.pro \ + common/ostproto.pro \ + common/ostprotogui.pro \ + server/drone.pro \ + client/ostinato.pro \ + binding/binding.pro + diff --git a/protobuf.pri b/protobuf.pri new file mode 100644 index 0000000..6316a38 --- /dev/null +++ b/protobuf.pri @@ -0,0 +1,42 @@ +# +# Qt qmake integration with Google Protocol Buffers compiler protoc +# +# To compile protocol buffers with qt qmake, specify PROTOS variable and +# include this file +# +# Example: +# PROTOS = a.proto b.proto +# include(protobuf.pri) +# +# By default protoc looks for .proto files (including the imported ones) in +# the current directory where protoc is run. If you need to include additional +# paths specify the PROTOPATH variable +# + +PROTOPATH += . +PROTOPATHS = +for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} + +protobuf_decl.name = protobuf header +protobuf_decl.input = PROTOS +protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h +#protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.commands = protoc --cpp_out="." --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf implementation +protobuf_impl.input = PROTOS +protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = GENERATED_SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl + +protobuf_py.name = protobuf python binding +protobuf_py.input = PROTOS +protobuf_py.output = ../binding/protocols/${QMAKE_FILE_BASE}_pb2.py +protobuf_py.commands = $$escape_expand(\n) +#protobuf_py.commands = protoc --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME} +protobuf_py.variable_out = GENERATED_FILES +QMAKE_EXTRA_COMPILERS += protobuf_py diff --git a/rpc/pbhelper.h b/rpc/pbhelper.h new file mode 100644 index 0000000..7ab80b3 --- /dev/null +++ b/rpc/pbhelper.h @@ -0,0 +1,170 @@ +/* +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 +*/ + +#ifndef _PB_HELPER_H +#define _PB_HELPER_H + +#include +#include + +#include + +#if 0 // not reqd. any longer? +class PbHelper +{ +public: + + // FIXME: Change msg from * to & + void ForceSetSingularDefault(::google::protobuf::Message *msg) + { + const ::google::protobuf::Descriptor *desc; + ::google::protobuf::Message::Reflection *refl; + + qDebug("In %s", __FUNCTION__); + + desc = msg->GetDescriptor(); + refl = msg->GetReflection(); + + for (int i=0; i < desc->field_count(); i++) + { + const ::google::protobuf::FieldDescriptor *f; + + f = desc->field(i); + + // Ensure field is singular and not already set + if (f->label() == + ::google::protobuf::FieldDescriptor::LABEL_REPEATED) + continue; + if (refl->HasField(f)) + continue; + + switch(f->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: + refl->SetDouble(f, refl->GetDouble(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: + refl->SetFloat(f, refl->GetFloat(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT32: + case ::google::protobuf::FieldDescriptor::TYPE_SINT32: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: + refl->SetInt32(f, refl->GetInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_INT64: + case ::google::protobuf::FieldDescriptor::TYPE_SINT64: + case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: + refl->SetInt64(f, refl->GetInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: + refl->SetUInt32(f, refl->GetUInt32(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_UINT64: + case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: + refl->SetUInt64(f, refl->GetUInt64(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + refl->SetBool(f, refl->GetBool(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_ENUM: + refl->SetEnum(f, refl->GetEnum(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + case ::google::protobuf::FieldDescriptor::TYPE_BYTES: + refl->SetString(f, refl->GetString(f)); + break; + + case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: + case ::google::protobuf::FieldDescriptor::TYPE_GROUP: + ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! + break; + + default: + qDebug("unhandled Field Type"); + break; + } + } + } + + bool update( + ::google::protobuf::Message *target, + ::google::protobuf::Message *source) + { + // FIXME(HI): Depracate: use MergeFrom() directly + qDebug("In %s", __FUNCTION__); + target->MergeFrom(*source); + return true; +#if 0 + ::google::protobuf::Message::Reflection *sourceRef; + ::google::protobuf::Message::Reflection *targetRef; + std::vector srcFieldList; + + + if (source->GetDescriptor()->full_name() != + target->GetDescriptor()->full_name()) + goto _error_exit; + + sourceRef = source->GetReflection(); + targetRef = target->GetReflection(); + + sourceRef->ListFields(&srcFieldList); + for (uint i=0; i < srcFieldList.size(); i++) + { + const ::google::protobuf::FieldDescriptor *srcField, *targetField; + + srcField = srcFieldList[i]; + targetField = target->GetDescriptor()->FindFieldByName( + srcField->name()); + + switch(targetField->type()) + { + case ::google::protobuf::FieldDescriptor::TYPE_UINT32: + targetRef->SetUInt32(targetField, + sourceRef->GetUInt32(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_BOOL: + targetRef->SetBool(targetField, + sourceRef->GetBool(srcField)); + break; + case ::google::protobuf::FieldDescriptor::TYPE_STRING: + targetRef->SetString(targetField, + sourceRef->GetString(srcField)); + break; + default: + qDebug("unhandled Field Type"); + break; + } + } + _error_exit: + qDebug("%s: error!", __FUNCTION__); + return false; +#endif + } +}; +#endif +#endif diff --git a/rpc/pbqtio.h b/rpc/pbqtio.h new file mode 100644 index 0000000..33d36a4 --- /dev/null +++ b/rpc/pbqtio.h @@ -0,0 +1,42 @@ +#ifndef _PBQTIO_H +#define _PBQTIO_H + +#include + +class PbQtInputStream : public google::protobuf::io::CopyingInputStream +{ +public: + PbQtInputStream(QIODevice *dev) + : dev_(dev) {}; + int Read(void *buffer, int size) { + _top: + if (dev_->bytesAvailable()) + return dev_->read(static_cast(buffer), size); + else + if (dev_->waitForReadyRead(-1)) + goto _top; + else + return -1; //return dev_->atEnd() ? 0 : -1; + } + +private: + QIODevice *dev_; +}; + +class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream +{ +public: + PbQtOutputStream(QIODevice *dev) + : dev_(dev) {}; + bool Write(const void *buffer, int size) { + if (dev_->write(static_cast(buffer), size) == size) + return true; + else + return false; + } + +private: + QIODevice *dev_; +}; + +#endif diff --git a/rpc/pbrpc.pro b/rpc/pbrpc.pro new file mode 100644 index 0000000..ff28d8a --- /dev/null +++ b/rpc/pbrpc.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += qt staticlib +QT += network +DEFINES += HAVE_REMOTE +LIBS += -lprotobuf +HEADERS += rpcserver.h rpcconn.h pbrpccontroller.h pbrpcchannel.h pbqtio.h +SOURCES += rpcserver.cpp rpcconn.cpp pbrpcchannel.cpp diff --git a/rpc/pbrpcchannel.cpp b/rpc/pbrpcchannel.cpp new file mode 100644 index 0000000..516a023 --- /dev/null +++ b/rpc/pbrpcchannel.cpp @@ -0,0 +1,390 @@ +/* +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 +*/ + +#include "pbrpcchannel.h" +#include "pbqtio.h" + +#include + +static uchar msgBuf[4096]; + +PbRpcChannel::PbRpcChannel(QHostAddress ip, quint16 port) +{ + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + controller = NULL; + done = NULL; + response = NULL; + + mServerAddress = ip; + mServerPort = port; + mpSocket = new QTcpSocket(this); + + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(mpSocket)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(mpSocket)); + outStream->SetOwnsCopyingStream(true); + + // FIXME: Not quite sure why this ain't working! + // QMetaObject::connectSlotsByName(this); + + connect(mpSocket, SIGNAL(connected()), + this, SLOT(on_mpSocket_connected())); + connect(mpSocket, SIGNAL(disconnected()), + this, SLOT(on_mpSocket_disconnected())); + connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); + connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); + + connect(mpSocket, SIGNAL(readyRead()), + this, SLOT(on_mpSocket_readyRead())); + +} + +PbRpcChannel::~PbRpcChannel() +{ + delete inStream; + delete outStream; + delete mpSocket; +} + +void PbRpcChannel::establish() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->connectToHost(mServerAddress, mServerPort); +} + +void PbRpcChannel::establish(QHostAddress ip, quint16 port) +{ + mServerAddress = ip; + mServerPort = port; + establish(); +} + +void PbRpcChannel::tearDown() +{ + qDebug("In %s", __FUNCTION__); + + mpSocket->disconnectFromHost(); +} + +void PbRpcChannel::CallMethod( + const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done) +{ + char* msg = (char*) &msgBuf[0]; + int len; + bool ret; + + if (isPending) + { + RpcCall call; + qDebug("RpcChannel: queueing rpc since method %d is pending;<----\n " + "queued method = %d\n" + "queued message = \n%s\n---->", + pendingMethodId, method->index(), req->DebugString().c_str()); + + call.method = method; + call.controller = controller; + call.request = req; + call.response = response; + call.done = done; + + pendingCallList.append(call); + qDebug("pendingCallList size = %d", pendingCallList.size()); + + Q_ASSERT(pendingCallList.size() < 100); + + return; + } + + if (!req->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in request <----"); + qDebug("req = \n%s", req->DebugString().c_str()); + qDebug("error = \n%s\n--->", req->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + done->Run(); + return; + } + + pendingMethodId = method->index(); + this->controller=controller; + this->done=done; + this->response=response; + isPending = true; + + len = req->ByteSize(); + *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type + *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id + *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len + + // Avoid printing stats since it happens every couple of seconds + if (pendingMethodId != 13) + { + qDebug("client(%s) sending %d bytes <----", __FUNCTION__, + PB_HDR_SIZE + len); + BUFDUMP(msg, PB_HDR_SIZE); + qDebug("method = %d\n req = \n%s\n---->", + method->index(), req->DebugString().c_str()); + } + + mpSocket->write(msg, PB_HDR_SIZE); + ret = req->SerializeToZeroCopyStream(outStream); + Q_ASSERT(ret == true); + Q_UNUSED(ret); + outStream->Flush(); +} + +void PbRpcChannel::on_mpSocket_readyRead() +{ + uchar *msg = (uchar*) &msgBuf; + int msgLen; + static bool parsing = false; + static quint16 type, method; + static quint32 len; + + //qDebug("%s: bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); + + if (!parsing) + { + // Do we have an entire header? If not, we'll wait ... + if (mpSocket->bytesAvailable() < PB_HDR_SIZE) + { + qDebug("client: not enough data available for a complete header"); + return; + } + + msgLen = mpSocket->read((char*)msg, PB_HDR_SIZE); + + Q_ASSERT(msgLen == PB_HDR_SIZE); + Q_UNUSED(msgLen); + + type = qFromBigEndian(msg+0); + method = qFromBigEndian(msg+2); + len = qFromBigEndian(msg+4); + + //BUFDUMP(msg, PB_HDR_SIZE); + //qDebug("type = %hu, method = %hu, len = %u", type, method, len); + + parsing = true; + } + + switch (type) + { + case PB_MSG_TYPE_BINBLOB: + { + static quint32 cumLen = 0; + QIODevice *blob; + + blob = static_cast(controller)->binaryBlob(); + Q_ASSERT(blob != NULL); + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msgBuf, sizeof(msgBuf)); + blob->write((char*)msgBuf, l); + cumLen += l; + } + + qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + cumLen = 0; + + if (!isPending) + { + qWarning("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qWarning("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + case PB_MSG_TYPE_RESPONSE: + //qDebug("client(%s) rcvd %d bytes", __FUNCTION__, msgLen); + //BUFDUMP(msg, msgLen); + + if (!isPending) + { + qWarning("not waiting for response"); + goto _error_exit; + } + + if (pendingMethodId != method) + { + qWarning("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit; + } + + if (len) + response->ParseFromBoundedZeroCopyStream(inStream, len); + + // Avoid printing stats + if (method != 13) + { + qDebug("client(%s): Received Msg <---- ", __FUNCTION__); + qDebug("method = %d\nresp = \n%s\n---->", + method, response->DebugString().c_str()); + } + + if (!response->IsInitialized()) + { + qWarning("RpcChannel: missing required fields in response <----"); + qDebug("resp = \n%s", response->DebugString().c_str()); + qDebug("error = \n%s\n--->", + response->InitializationErrorString().c_str()); + + controller->SetFailed("Required fields missing"); + } + break; + + case PB_MSG_TYPE_ERROR: + { + static quint32 cumLen = 0; + static QByteArray error; + + while ((cumLen < len) && mpSocket->bytesAvailable()) + { + int l; + + l = mpSocket->read((char*)msgBuf, sizeof(msgBuf)); + error.append(QByteArray((char*)msgBuf,l)); + cumLen += l; + } + + qDebug("%s: error rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); + + if (cumLen < len) + return; + + static_cast(controller)->SetFailed( + QString::fromUtf8(error, len)); + + cumLen = 0; + error.resize(0); + + if (!isPending) + { + qWarning("not waiting for response"); + goto _error_exit2; + } + + if (pendingMethodId != method) + { + qWarning("invalid method id %d (expected = %d)", method, + pendingMethodId); + goto _error_exit2; + } + + break; + } + + default: + qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); + goto _error_exit; + + } + + done->Run(); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + parsing = false; + + if (pendingCallList.size()) + { + RpcCall call = pendingCallList.takeFirst(); + qDebug("RpcChannel: executing queued method <----\n" + "method = %d\n" + "req = \n%s\n---->", + call.method->index(), call.request->DebugString().c_str()); + CallMethod(call.method, call.controller, call.request, call.response, + call.done); + } + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + parsing = false; + qDebug("client(%s) discarding received msg <----", __FUNCTION__); + qDebug("method = %d\nreq = \n%s\n---->", + method, response->DebugString().c_str()); + return; +} + +void PbRpcChannel::on_mpSocket_stateChanged( + QAbstractSocket::SocketState socketState) +{ + qDebug("In %s", __FUNCTION__); + emit stateChanged(socketState); +} + +void PbRpcChannel::on_mpSocket_connected() +{ + qDebug("In %s", __FUNCTION__); + emit connected(); +} + +void PbRpcChannel::on_mpSocket_disconnected() +{ + qDebug("In %s", __FUNCTION__); + + pendingMethodId = -1; + controller = NULL; + response = NULL; + isPending = false; + // \todo convert parsing from static to data member + //parsing = false + pendingCallList.clear(); + + emit disconnected(); +} + +void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) +{ + qDebug("In %s", __FUNCTION__); + emit error(socketError); +} + diff --git a/rpc/pbrpcchannel.h b/rpc/pbrpcchannel.h new file mode 100644 index 0000000..e3f9096 --- /dev/null +++ b/rpc/pbrpcchannel.h @@ -0,0 +1,106 @@ +/* +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 +*/ + +#ifndef _PB_RPC_CHANNEL_H +#define _PB_RPC_CHANNEL_H + +#include +#include + +#include +#include +#include +#include + +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel +{ + Q_OBJECT + + // If isPending is TRUE, then controller, done, response + // and pendingMethodId correspond to the last method called by + // the service stub + bool isPending; + int pendingMethodId; + + // controller, done, response are set to the corresponding values + // passed by the stub to CallMethod(). They are reset to NULL when + // we get a response back from the server in on_mpSocket_readyRead() + // after calling done->Run(). + + /*! \todo (MED) : change controller, done and response to references + instead of pointers? */ + ::google::protobuf::RpcController *controller; + ::google::protobuf::Closure *done; + ::google::protobuf::Message *response; + + typedef struct _RpcCall { + const ::google::protobuf::MethodDescriptor *method; + ::google::protobuf::RpcController *controller; + const ::google::protobuf::Message *request; + ::google::protobuf::Message *response; + ::google::protobuf::Closure *done; + } RpcCall; + QList pendingCallList; + + QHostAddress mServerAddress; + quint16 mServerPort; + QTcpSocket *mpSocket; + + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + +public: + PbRpcChannel(QHostAddress ip, quint16 port); + ~PbRpcChannel(); + + void establish(); + void establish(QHostAddress ip, quint16 port); + void tearDown(); + + const QHostAddress& serverAddress() const { return mServerAddress; } + quint16 serverPort() const { return mServerPort; } + + QAbstractSocket::SocketState state() const + { return mpSocket->state(); } + + void CallMethod(const ::google::protobuf::MethodDescriptor *method, + ::google::protobuf::RpcController *controller, + const ::google::protobuf::Message *req, + ::google::protobuf::Message *response, + ::google::protobuf::Closure* done); + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); + void stateChanged(QAbstractSocket::SocketState socketState); + +private slots: + void on_mpSocket_connected(); + void on_mpSocket_disconnected(); + void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); + void on_mpSocket_error(QAbstractSocket::SocketError socketError); + + void on_mpSocket_readyRead(); +}; + +#endif diff --git a/rpc/pbrpccommon.h b/rpc/pbrpccommon.h new file mode 100644 index 0000000..07c8013 --- /dev/null +++ b/rpc/pbrpccommon.h @@ -0,0 +1,40 @@ +/* +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 +*/ + +#ifndef _PB_RPC_COMMON_H +#define _PB_RPC_COMMON_H + +// Print a HexDump +#define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ + (len)).toHex()).toAscii().data()); + +/* +** RPC Header (8) +** - MSG_TYPE (2) +** - METHOD_ID (2) +** - LEN (4) [not including this header] +*/ +#define PB_HDR_SIZE 8 + +#define PB_MSG_TYPE_REQUEST 1 +#define PB_MSG_TYPE_RESPONSE 2 +#define PB_MSG_TYPE_BINBLOB 3 +#define PB_MSG_TYPE_ERROR 4 + +#endif diff --git a/rpc/pbrpccontroller.h b/rpc/pbrpccontroller.h new file mode 100644 index 0000000..788fc2b --- /dev/null +++ b/rpc/pbrpccontroller.h @@ -0,0 +1,88 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _PB_RPC_CONTROLLER_H +#define _PB_RPC_CONTROLLER_H + +#include +#include + +class QIODevice; + +/*! +PbRpcController takes ownership of the 'request' and 'response' messages and +will delete them when it itself is destroyed +*/ +class PbRpcController : public ::google::protobuf::RpcController +{ +public: + PbRpcController(::google::protobuf::Message *request, + ::google::protobuf::Message *response) { + request_ = request; + response_ = response; + Reset(); + } + ~PbRpcController() { delete request_; delete response_; } + + ::google::protobuf::Message* request() { return request_; } + ::google::protobuf::Message* response() { return response_; } + + // Client Side Methods + void Reset() { + failed = false; + disconnect = false; + blob = NULL; + errStr = ""; + } + bool Failed() const { return failed; } + void StartCancel() { /*! \todo (MED) */} + std::string ErrorText() const { return errStr.toStdString(); } + + // Server Side Methods + void SetFailed(const QString &reason) + { failed = true; errStr = reason; qWarning("%s", qPrintable(errStr)); } + void SetFailed(const std::string &reason) + { SetFailed(QString::fromStdString(reason)); } + QString ErrorString() const { return errStr; } + bool IsCanceled() const { return false; }; + void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { + /*! \todo (MED) */ + } + void TriggerDisconnect() { + disconnect = true; + } + bool Disconnect() const { + return disconnect; + } + + // srivatsp added + QIODevice* binaryBlob() { return blob; }; + void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; + +private: + bool failed; + bool disconnect; + QIODevice *blob; + QString errStr; + ::google::protobuf::Message *request_; + ::google::protobuf::Message *response_; + +}; + +#endif diff --git a/rpc/rpcconn.cpp b/rpc/rpcconn.cpp new file mode 100644 index 0000000..f55a224 --- /dev/null +++ b/rpc/rpcconn.cpp @@ -0,0 +1,372 @@ +/* +Copyright (C) 2010, 2014 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 "rpcconn.h" + +#include "pbqtio.h" +#include "pbrpccommon.h" +#include "pbrpccontroller.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +static QThreadStorage connId; + +RpcConnection::RpcConnection(int socketDescriptor, + ::google::protobuf::Service *service) + : socketDescriptor(socketDescriptor), + service(service) +{ + inStream = NULL; + outStream = NULL; + + isPending = false; + pendingMethodId = -1; // don't care as long as isPending is false + + isCompatCheckDone = false; +} + +RpcConnection::~RpcConnection() +{ + qDebug("destroying connection to %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + // If still connected, disconnect + if (clientSock->state() != QAbstractSocket::UnconnectedState) { + clientSock->disconnectFromHost(); + clientSock->waitForDisconnected(); + } + + delete inStream; + delete outStream; + + delete clientSock; +} + +void RpcConnection::start() +{ + QString id = QString("[%1:%2] "); + clientSock = new QTcpSocket; + if (!clientSock->setSocketDescriptor(socketDescriptor)) { + qWarning("Unable to initialize TCP socket for incoming connection"); + return; + } + qDebug("clientSock Thread = %p", clientSock->thread()); + + connId.setLocalData(new QString(id.arg(clientSock->peerAddress().toString()) + .arg(clientSock->peerPort()))); + + qDebug("accepting new connection from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + inStream = new google::protobuf::io::CopyingInputStreamAdaptor( + new PbQtInputStream(clientSock)); + inStream->SetOwnsCopyingStream(true); + outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( + new PbQtOutputStream(clientSock)); + outStream->SetOwnsCopyingStream(true); + + connect(clientSock, SIGNAL(readyRead()), + this, SLOT(on_clientSock_dataAvail())); + connect(clientSock, SIGNAL(disconnected()), + this, SLOT(on_clientSock_disconnected())); + connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_clientSock_error(QAbstractSocket::SocketError))); +} + +void RpcConnection::writeHeader(char* header, quint16 type, quint16 method, + quint32 length) +{ + *((quint16*)(header+0)) = qToBigEndian(type); + *((quint16*)(header+2)) = qToBigEndian(method); + *((quint32*)(header+4)) = qToBigEndian(length); +} + +void RpcConnection::sendRpcReply(PbRpcController *controller) +{ + google::protobuf::Message *response = controller->response(); + QIODevice *blob; + char msgBuf[PB_HDR_SIZE]; + char* const msg = &msgBuf[0]; + int len; + + if (controller->Failed()) + { + QByteArray err = controller->ErrorString().toUtf8(); + + qWarning("rpc failed (%s)", qPrintable(controller->ErrorString())); + len = err.size(); + writeHeader(msg, PB_MSG_TYPE_ERROR, pendingMethodId, len); + clientSock->write(msg, PB_HDR_SIZE); + clientSock->write(err.constData(), len); + + goto _exit; + } + + blob = controller->binaryBlob(); + if (blob) + { + len = blob->size(); + qDebug("is binary blob of len %d", len); + + writeHeader(msg, PB_MSG_TYPE_BINBLOB, pendingMethodId, len); + clientSock->write(msg, PB_HDR_SIZE); + + blob->seek(0); + while (!blob->atEnd()) + { + int l; + + len = blob->read(msg, sizeof(msgBuf)); + l = clientSock->write(msg, len); + Q_ASSERT(l == len); + Q_UNUSED(l); + } + + goto _exit; + } + + if (!response->IsInitialized()) + { + qWarning("response missing required fields!! <----"); + qDebug("response = \n%s" + "missing = \n%s---->", + response->DebugString().c_str(), + response->InitializationErrorString().c_str()); + qFatal("exiting"); + goto _exit; + } + + len = response->ByteSize(); + writeHeader(msg, PB_MSG_TYPE_RESPONSE, pendingMethodId, len); + + // Avoid printing stats since it happens once every couple of seconds + if (pendingMethodId != 13) + { + qDebug("Server(%s): sending %d bytes to client <----", + __FUNCTION__, len + PB_HDR_SIZE); + BUFDUMP(msg, 8); + qDebug("method = %d\nreq = \n%s---->", + pendingMethodId, response->DebugString().c_str()); + } + + clientSock->write(msg, PB_HDR_SIZE); + response->SerializeToZeroCopyStream(outStream); + outStream->Flush(); + + if (pendingMethodId == 15) + isCompatCheckDone = true; + +_exit: + if (controller->Disconnect()) + clientSock->disconnectFromHost(); + + delete controller; + isPending = false; +} + +void RpcConnection::on_clientSock_disconnected() +{ + qDebug("connection closed from %s: %d", + clientSock->peerAddress().toString().toAscii().constData(), + clientSock->peerPort()); + + deleteLater(); + emit closed(); +} + +void RpcConnection::on_clientSock_error(QAbstractSocket::SocketError socketError) +{ + qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), + socketError); +} + +void RpcConnection::on_clientSock_dataAvail() +{ + uchar msg[PB_HDR_SIZE]; + int msgLen; + quint16 type, method; + quint32 len; + const ::google::protobuf::MethodDescriptor *methodDesc; + ::google::protobuf::Message *req, *resp; + PbRpcController *controller; + QString error; + bool disconnect = false; + + // Do we have enough bytes for a msg header? + // If yes, peek into the header and get msg length + if (clientSock->bytesAvailable() < PB_HDR_SIZE) + return; + + msgLen = clientSock->peek((char*)msg, PB_HDR_SIZE); + if (msgLen != PB_HDR_SIZE) { + qWarning("asked to peek %d bytes, was given only %d bytes", + PB_HDR_SIZE, msgLen); + return; + } + + len = qFromBigEndian(&msg[4]); + + // Is the full msg available to read? If not, wait till such time + if (clientSock->bytesAvailable() < (PB_HDR_SIZE+len)) + return; + + msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); + Q_ASSERT(msgLen == PB_HDR_SIZE); + + type = qFromBigEndian(&msg[0]); + method = qFromBigEndian(&msg[2]); + len = qFromBigEndian(&msg[4]); + //qDebug("type = %d, method = %d, len = %d", type, method, len); + + if (type != PB_MSG_TYPE_REQUEST) + { + qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, + type, PB_MSG_TYPE_REQUEST); + error = QString("unexpected msg type %1; expected %2") + .arg(type).arg(PB_MSG_TYPE_REQUEST); + goto _error_exit; + } + + // If RPC is not checkVersion, ensure compat check is already done + if (!isCompatCheckDone && method != 15) { + qDebug("server(%s): version compatibility check pending", + __FUNCTION__); + error = "version compatibility check pending"; + disconnect = true; + goto _error_exit; + } + + if (method >= service->GetDescriptor()->method_count()) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + error = QString("invalid RPC method %1").arg(method); + goto _error_exit; + } + + methodDesc = service->GetDescriptor()->method(method); + if (!methodDesc) + { + qDebug("server(%s): invalid method id %d", __FUNCTION__, method); + error = QString("invalid RPC method %1").arg(method); + goto _error_exit; + } + + if (isPending) + { + qDebug("server(%s): rpc pending, try again", __FUNCTION__); + error = QString("RPC %1() is pending; only one RPC allowed at a time; " + "try again!").arg(QString::fromStdString( + service->GetDescriptor()->method( + pendingMethodId)->name())); + goto _error_exit; + } + + pendingMethodId = method; + isPending = true; + + req = service->GetRequestPrototype(methodDesc).New(); + resp = service->GetResponsePrototype(methodDesc).New(); + + if (len) { + bool ok = req->ParseFromBoundedZeroCopyStream(inStream, len); + if (!ok) + qWarning("ParseFromBoundedZeroCopyStream fail " + "for method %d and len %d", method, len); + } + + if (!req->IsInitialized()) + { + qWarning("Missing required fields in request <----"); + qDebug("method = %d\n" + "req = \n%s" + "missing = \n%s----->", + method, req->DebugString().c_str(), + req->InitializationErrorString().c_str()); + error = QString("RPC %1() missing required fields in request - %2") + .arg(QString::fromStdString( + service->GetDescriptor()->method( + pendingMethodId)->name()), + QString(req->InitializationErrorString().c_str())); + delete req; + delete resp; + + goto _error_exit2; + } + + if (method != 13) { + qDebug("Server(%s): successfully received/parsed msg <----", __FUNCTION__); + qDebug("method = %d\n" + "req = \n%s---->", + method, + req->DebugString().c_str()); + } + + controller = new PbRpcController(req, resp); + + //qDebug("before service->callmethod()"); + + service->CallMethod(methodDesc, controller, req, resp, + google::protobuf::NewCallback(this, &RpcConnection::sendRpcReply, + controller)); + + return; + +_error_exit: + inStream->Skip(len); +_error_exit2: + qDebug("server(%s): return error %s for msg from client", __FUNCTION__, + qPrintable(error)); + pendingMethodId = method; + isPending = true; + controller = new PbRpcController(NULL, NULL); + controller->SetFailed(error); + if (disconnect) + controller->TriggerDisconnect(); + sendRpcReply(controller); + return; +} + +void RpcConnection::connIdMsgHandler(QtMsgType /*type*/, const char* msg) +{ + if (connId.hasLocalData()) { + QString newMsg(*connId.localData()); + newMsg.append(msg); + newMsg.replace(QChar('\n'), QString("\n").append(*connId.localData())); + fprintf(stderr, "%s\n", qPrintable(newMsg)); + fflush(stderr); + return; + } + + fprintf(stderr, "%s\n", msg); + fflush(stderr); +} diff --git a/rpc/rpcconn.h b/rpc/rpcconn.h new file mode 100644 index 0000000..e41d8e2 --- /dev/null +++ b/rpc/rpcconn.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _RPC_CONNECTION_H +#define _RPC_CONNECTION_H + +#include + +// forward declarations +class PbRpcController; +class QTcpSocket; +namespace google { + namespace protobuf { + class Service; + namespace io { + class CopyingInputStreamAdaptor; + class CopyingOutputStreamAdaptor; + } + } +} + +class RpcConnection : public QObject +{ + Q_OBJECT + +public: + RpcConnection(int socketDescriptor, ::google::protobuf::Service *service); + virtual ~RpcConnection(); + static void connIdMsgHandler(QtMsgType type, const char* msg); + +private: + void writeHeader(char* header, quint16 type, quint16 method, + quint32 length); + void sendRpcReply(PbRpcController *controller); + +signals: + void closed(); + +private slots: + void start(); + void on_clientSock_dataAvail(); + void on_clientSock_error(QAbstractSocket::SocketError socketError); + void on_clientSock_disconnected(); + +private: + int socketDescriptor; + QTcpSocket *clientSock; + + ::google::protobuf::Service *service; + ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; + ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; + + bool isPending; + int pendingMethodId; + + bool isCompatCheckDone; +}; + +#endif diff --git a/rpc/rpcserver.cpp b/rpc/rpcserver.cpp new file mode 100644 index 0000000..dad4b76 --- /dev/null +++ b/rpc/rpcserver.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2010, 2014 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 "rpcserver.h" + +#include "rpcconn.h" + +#include + +// FIXME: QThreadX till we change minimum version of Qt from Qt4.3+ to Qt4.4+ +class QThreadX: public QThread +{ +protected: + virtual ~QThreadX() { qDebug("QThreadX going down!"); } + void run() { exec(); } +}; + +RpcServer::RpcServer() +{ + service = NULL; + + qInstallMsgHandler(RpcConnection::connIdMsgHandler); +} + +RpcServer::~RpcServer() +{ +} + +bool RpcServer::registerService(::google::protobuf::Service *service, + quint16 tcpPortNum) +{ + this->service = service; + + if (!listen(QHostAddress::Any, tcpPortNum)) + { + qDebug("Unable to start the server: %s", + errorString().toAscii().constData()); + return false; + } + + qDebug("The server is running on %s: %d", + serverAddress().toString().toAscii().constData(), + serverPort()); + return true; +} + +void RpcServer::incomingConnection(int socketDescriptor) +{ + QThread *thread = new QThreadX; // FIXME:QThreadX pending Qt4.4+ + RpcConnection *conn = new RpcConnection(socketDescriptor, service); + + conn->moveToThread(thread); + + connect(thread, SIGNAL(started()), conn, SLOT(start())); + + // NOTE: conn "self-destructs" after emitting closed + // use 'closed' to stop execution of the thread + connect(conn, SIGNAL(closed()), thread, SLOT(quit())); + + // setup thread to "self-destruct" when it is done + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + thread->start(); +} diff --git a/rpc/rpcserver.h b/rpc/rpcserver.h new file mode 100644 index 0000000..0a4acdd --- /dev/null +++ b/rpc/rpcserver.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2010, 2014 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 +*/ + +#ifndef _RPC_SERVER_H +#define _RPC_SERVER_H + +#include + +// forward declaration +namespace google { + namespace protobuf { + class Service; + } +} + +class RpcServer : public QTcpServer +{ + Q_OBJECT + +public: + RpcServer(); //! \todo (LOW) use 'parent' param + virtual ~RpcServer(); + + bool registerService(::google::protobuf::Service *service, + quint16 tcpPortNum); + +protected: + void incomingConnection(int socketDescriptor); + +private: + ::google::protobuf::Service *service; +}; + +#endif diff --git a/server/abstractport.cpp b/server/abstractport.cpp new file mode 100644 index 0000000..ef3e881 --- /dev/null +++ b/server/abstractport.cpp @@ -0,0 +1,594 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#define __STDC_FORMAT_MACROS + +#include "abstractport.h" + +#include "../common/streambase.h" +#include "../common/abstractprotocol.h" + +#include +#include + +#include +#include +#include + +AbstractPort::AbstractPort(int id, const char *device) +{ + isUsable_ = true; + data_.mutable_port_id()->set_id(id); + data_.set_name(device); + + //! \todo (LOW) admin enable/disable of port + data_.set_is_enabled(true); + + data_.set_is_exclusive_control(false); + + isSendQueueDirty_ = false; + linkState_ = OstProto::LinkStateUnknown; + minPacketSetSize_ = 1; + + maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats + memset((void*) &stats_, 0, sizeof(stats_)); + resetStats(); +} + +AbstractPort::~AbstractPort() +{ +} + +void AbstractPort::init() +{ +} + +bool AbstractPort::modify(const OstProto::Port &port) +{ + bool ret = false; + + //! \todo Use reflection to find out which fields are set + if (port.has_is_exclusive_control()) + { + bool val = port.is_exclusive_control(); + + ret = setExclusiveControl(val); + if (ret) + data_.set_is_exclusive_control(val); + } + + if (port.has_transmit_mode()) + data_.set_transmit_mode(port.transmit_mode()); + + return ret; +} + +StreamBase* AbstractPort::streamAtIndex(int index) +{ + Q_ASSERT(index < streamList_.size()); + return streamList_.at(index); +} + +StreamBase* AbstractPort::stream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + if ((uint)streamId == streamList_.at(i)->id()) + return streamList_.at(i); + } + + return NULL; +} + +bool AbstractPort::addStream(StreamBase *stream) +{ + streamList_.append(stream); + isSendQueueDirty_ = true; + return true; +} + +bool AbstractPort::deleteStream(int streamId) +{ + for (int i = 0; i < streamList_.size(); i++) + { + StreamBase *stream; + + if ((uint)streamId == streamList_.at(i)->id()) + { + stream = streamList_.takeAt(i); + delete stream; + + isSendQueueDirty_ = true; + return true; + } + } + + return false; +} + +void AbstractPort::addNote(QString note) +{ + QString notes = QString::fromStdString(data_.notes()); + + note.prepend("
  • "); + note.append("
  • "); + + if (notes.isEmpty()) + notes="Limitation(s)
      "; + else + notes.remove("
    "); + + notes.append(note); + notes.append(""); + + data_.set_notes(notes.toStdString()); +} + +void AbstractPort::updatePacketList() +{ + switch(data_.transmit_mode()) + { + case OstProto::kSequentialTransmit: + updatePacketListSequential(); + break; + case OstProto::kInterleavedTransmit: + updatePacketListInterleaved(); + break; + default: + Q_ASSERT(false); // Unreachable!!! + break; + } +} + +void AbstractPort::updatePacketListSequential() +{ + long sec = 0; + long nsec = 0; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (streamList_[i]->isEnabled()) + { + int len = 0; + ulong n, x, y; + ulong burstSize; + double ibg = 0; + quint64 ibg1 = 0, ibg2 = 0; + quint64 nb1 = 0, nb2 = 0; + double ipg = 0; + quint64 ipg1 = 0, ipg2 = 0; + quint64 npx1 = 0, npx2 = 0; + quint64 npy1 = 0, npy2 = 0; + quint64 loopDelay; + ulong frameVariableCount = streamList_[i]->frameVariableCount(); + + // We derive n, x, y such that + // n * x + y = total number of packets to be sent + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + burstSize = streamList_[i]->burstSize(); + x = AbstractProtocol::lcm(frameVariableCount, burstSize); + n = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) / x; + y = ulong(burstSize * streamList_[i]->burstRate() + * streamList_[i]->numBursts()) % x; + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + ibg1 = quint64(ceil(ibg)); + ibg2 = quint64(floor(ibg)); + nb1 = quint64((ibg - double(ibg2)) * double(x)); + nb2 = x - nb1; + } + loopDelay = ibg2; + break; + case OstProto::StreamControl::e_su_packets: + x = frameVariableCount; + n = 2; + while (x < minPacketSetSize_) + x = frameVariableCount*n++; + n = streamList_[i]->numPackets() / x; + y = streamList_[i]->numPackets() % x; + burstSize = x + y; + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + ipg1 = quint64(ceil(ipg)); + ipg2 = quint64(floor(ipg)); + npx1 = quint64((ipg - double(ipg2)) * double(x)); + npx2 = x - npx1; + npy1 = quint64((ipg - double(ipg2)) * double(y)); + npy2 = y - npy1; + } + loopDelay = ipg2; + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + + qDebug("\nframeVariableCount = %lu", frameVariableCount); + qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", + n, x, y, burstSize); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, ibg1); + qDebug("nb1 = %" PRIu64, nb1); + qDebug("ibg2 = %" PRIu64, ibg2); + qDebug("nb2 = %" PRIu64 "\n", nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, ipg1); + qDebug("npx1 = %" PRIu64, npx1); + qDebug("npy1 = %" PRIu64, npy1); + qDebug("ipg2 = %" PRIu64, ipg2); + qDebug("npx2 = %" PRIu64, npx2); + qDebug("npy2 = %" PRIu64 "\n", npy2); + + if (n > 1) + loopNextPacketSet(x, n, 0, loopDelay); + else if (n == 0) + x = 0; + + for (uint j = 0; j < (x+y); j++) + { + + if (j == 0 || frameVariableCount > 1) + { + len = streamList_[i]->frameValue( + pktBuf_, sizeof(pktBuf_), j); + } + if (len <= 0) + continue; + + qDebug("q(%d, %d) sec = %lu nsec = %lu", + i, j, sec, nsec); + + appendToPacketList(sec, nsec, pktBuf_, len); + + if ((j > 0) && (((j+1) % burstSize) == 0)) + { + nsec += (j < nb1) ? ibg1 : ibg2; + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + else + { + if (j < x) + nsec += (j < npx1) ? ipg1 : ipg2; + else + nsec += ((j-x) < npy1) ? ipg1 : ipg2; + + while (nsec >= long(1e9)) + { + sec++; + nsec -= long(1e9); + } + } + } + + switch(streamList_[i]->nextWhat()) + { + case ::OstProto::StreamControl::e_nw_stop: + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_id: + /*! \todo (MED): define and use + streamList_[i].d.control().goto_stream_id(); */ + + /*! \todo (MED): assumes goto Id is less than current!!!! + To support goto to any id, do + if goto_id > curr_id then + i = goto_id; + goto restart; + else + returnToQIdx = 0; + */ + + setPacketListLoopMode(true, 0, + streamList_[i]->sendUnit() == + StreamBase::e_su_bursts ? ibg1 : ipg1); + goto _stop_no_more_pkts; + + case ::OstProto::StreamControl::e_nw_goto_next: + break; + + default: + qFatal("---------- %s: Unhandled case (%d) -----------", + __FUNCTION__, streamList_[i]->nextWhat() ); + break; + } + + } // if (stream is enabled) + } // for (numStreams) + +_stop_no_more_pkts: + isSendQueueDirty_ = false; +} + +void AbstractPort::updatePacketListInterleaved() +{ + int numStreams = 0; + quint64 minGap = ULLONG_MAX; + quint64 duration = quint64(1e9); + QList ibg1, ibg2; + QList nb1, nb2; + QList ipg1, ipg2; + QList np1, np2; + QList schedSec, schedNsec; + QList pktCount, burstCount; + QList burstSize; + QList isVariable; + QList pktBuf; + QList pktLen; + + qDebug("In %s", __FUNCTION__); + + // First sort the streams by ordinalValue + qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); + + clearPacketList(); + + for (int i = 0; i < streamList_.size(); i++) + { + if (!streamList_[i]->isEnabled()) + continue; + + double numBursts = 0; + double numPackets = 0; + + quint64 _burstSize = 0; + double ibg = 0; + quint64 _ibg1 = 0, _ibg2 = 0; + quint64 _nb1 = 0, _nb2 = 0; + double ipg = 0; + quint64 _ipg1 = 0, _ipg2 = 0; + quint64 _np1 = 0, _np2 = 0; + + switch (streamList_[i]->sendUnit()) + { + case OstProto::StreamControl::e_su_bursts: + numBursts = streamList_[i]->burstRate(); + if (streamList_[i]->burstRate() > 0) + { + ibg = 1e9/double(streamList_[i]->burstRate()); + _ibg1 = quint64(ceil(ibg)); + _ibg2 = quint64(floor(ibg)); + _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); + _nb2 = quint64(numBursts) - _nb1; + _burstSize = streamList_[i]->burstSize(); + } + break; + case OstProto::StreamControl::e_su_packets: + numPackets = streamList_[i]->packetRate(); + if (streamList_[i]->packetRate() > 0) + { + ipg = 1e9/double(streamList_[i]->packetRate()); + _ipg1 = llrint(ceil(ipg)); + _ipg2 = quint64(floor(ipg)); + _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); + _np2 = quint64(numPackets) - _np1; + _burstSize = 1; + } + break; + default: + qWarning("Unhandled stream control unit %d", + streamList_[i]->sendUnit()); + continue; + } + qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); + + qDebug("ibg = %g", ibg); + qDebug("ibg1 = %" PRIu64, _ibg1); + qDebug("nb1 = %" PRIu64, _nb1); + qDebug("ibg2 = %" PRIu64, _ibg2); + qDebug("nb2 = %" PRIu64 "\n", _nb2); + + qDebug("ipg = %g", ipg); + qDebug("ipg1 = %" PRIu64, _ipg1); + qDebug("np1 = %" PRIu64, _np1); + qDebug("ipg2 = %" PRIu64, _ipg2); + qDebug("np2 = %" PRIu64 "\n", _np2); + + + if (_ibg2 && (_ibg2 < minGap)) + minGap = _ibg2; + + if (_ibg1 && (_ibg1 > duration)) + duration = _ibg1; + + ibg1.append(_ibg1); + ibg2.append(_ibg2); + + nb1.append(_nb1); + nb2.append(_nb1); + + burstSize.append(_burstSize); + + if (_ipg2 && (_ipg2 < minGap)) + minGap = _ipg2; + + if (_np1) + { + if (_ipg1 && (_ipg1 > duration)) + duration = _ipg1; + } + else + { + if (_ipg2 && (_ipg2 > duration)) + duration = _ipg2; + } + + ipg1.append(_ipg1); + ipg2.append(_ipg2); + + np1.append(_np1); + np2.append(_np1); + + schedSec.append(0); + schedNsec.append(0); + + pktCount.append(0); + burstCount.append(0); + + if (streamList_[i]->isFrameVariable()) + { + isVariable.append(true); + pktBuf.append(QByteArray()); + pktLen.append(0); + } + else + { + isVariable.append(false); + pktBuf.append(QByteArray()); + pktBuf.last().resize(kMaxPktSize); + pktLen.append(streamList_[i]->frameValue( + (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); + } + + numStreams++; + } // for i + + qDebug("minGap = %" PRIu64, minGap); + qDebug("duration = %" PRIu64, duration); + + uchar* buf; + int len; + quint64 durSec = duration/ulong(1e9); + quint64 durNsec = duration % ulong(1e9); + quint64 sec = 0; + quint64 nsec = 0; + quint64 lastPktTxSec = 0; + quint64 lastPktTxNsec = 0; + do + { + for (int i = 0; i < numStreams; i++) + { + // If a packet is not scheduled yet, look at the next stream + if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) + continue; + + for (uint j = 0; j < burstSize[i]; j++) + { + if (isVariable.at(i)) + { + buf = pktBuf_; + len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), + pktCount[i]); + } + else + { + buf = (uchar*) pktBuf.at(i).data(); + len = pktLen.at(i); + } + + if (len <= 0) + continue; + + qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); + appendToPacketList(sec, nsec, buf, len); + lastPktTxSec = sec; + lastPktTxNsec = nsec; + + pktCount[i]++; + schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? + ipg1.at(i) : ipg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + burstCount[i]++; + schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? + ibg1.at(i) : ibg2.at(i); + while (schedNsec.at(i) >= 1e9) + { + schedSec[i]++; + schedNsec[i] -= long(1e9); + } + } + + nsec += minGap; + while (nsec >= 1e9) + { + sec++; + nsec -= long(1e9); + } + } while ((sec < durSec) || (nsec < durNsec)); + + qint64 delaySec = durSec - lastPktTxSec; + qint64 delayNsec = durNsec - lastPktTxNsec; + while (delayNsec < 0) + { + delayNsec += long(1e9); + delaySec--; + } + qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); + setPacketListLoopMode(true, delaySec, delayNsec); + isSendQueueDirty_ = false; +} + +void AbstractPort::stats(PortStats *stats) +{ + stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? + stats_.rxPkts - epochStats_.rxPkts : + stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); + stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? + stats_.rxBytes - epochStats_.rxBytes : + stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); + stats->rxPps = stats_.rxPps; + stats->rxBps = stats_.rxBps; + + stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? + stats_.txPkts - epochStats_.txPkts : + stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); + stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? + stats_.txBytes - epochStats_.txBytes : + stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); + stats->txPps = stats_.txPps; + stats->txBps = stats_.txBps; + + stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? + stats_.rxDrops - epochStats_.rxDrops : + stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); + stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? + stats_.rxErrors - epochStats_.rxErrors : + stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); + stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? + stats_.rxFifoErrors - epochStats_.rxFifoErrors : + stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); + stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? + stats_.rxFrameErrors - epochStats_.rxFrameErrors : + stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); +} diff --git a/server/abstractport.h b/server/abstractport.h new file mode 100644 index 0000000..44f0c1d --- /dev/null +++ b/server/abstractport.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2010-2012 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 +*/ + +#ifndef _SERVER_ABSTRACT_PORT_H +#define _SERVER_ABSTRACT_PORT_H + +#include +#include + +#include "../common/protocol.pb.h" + +class StreamBase; +class QIODevice; + +class AbstractPort +{ +public: + struct PortStats + { + quint64 rxPkts; + quint64 rxBytes; + quint64 rxPps; + quint64 rxBps; + + quint64 rxDrops; + quint64 rxErrors; + quint64 rxFifoErrors; + quint64 rxFrameErrors; + + quint64 txPkts; + quint64 txBytes; + quint64 txPps; + quint64 txBps; + }; + + AbstractPort(int id, const char *device); + virtual ~AbstractPort(); + + bool isUsable() { return isUsable_; } + + virtual void init(); + + int id() { return data_.port_id().id(); } + const char* name() { return data_.name().c_str(); } + void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } + + bool modify(const OstProto::Port &port); + + virtual OstProto::LinkState linkState() { return linkState_; } + virtual bool hasExclusiveControl() = 0; + virtual bool setExclusiveControl(bool exclusive) = 0; + + int streamCount() { return streamList_.size(); } + StreamBase* streamAtIndex(int index); + StreamBase* stream(int streamId); + bool addStream(StreamBase *stream); + bool deleteStream(int streamId); + + bool isDirty() { return isSendQueueDirty_; } + void setDirty() { isSendQueueDirty_ = true; } + + virtual void clearPacketList() = 0; + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) = 0; + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) = 0; + virtual void setPacketListLoopMode(bool loop, + quint64 secDelay, quint64 nsecDelay) = 0; + void updatePacketList(); + + virtual void startTransmit() = 0; + virtual void stopTransmit() = 0; + virtual bool isTransmitOn() = 0; + + virtual void startCapture() = 0; + virtual void stopCapture() = 0; + virtual bool isCaptureOn() = 0; + virtual QIODevice* captureData() = 0; + + void stats(PortStats *stats); + void resetStats() { epochStats_ = stats_; } + +protected: + void addNote(QString note); + + void updatePacketListSequential(); + void updatePacketListInterleaved(); + + bool isUsable_; + OstProto::Port data_; + OstProto::LinkState linkState_; + ulong minPacketSetSize_; + + quint64 maxStatsValue_; + struct PortStats stats_; + //! \todo Need lock for stats access/update + +private: + bool isSendQueueDirty_; + + static const int kMaxPktSize = 16384; + uchar pktBuf_[kMaxPktSize]; + + /*! \note StreamBase::id() and index into streamList[] are NOT same! */ + QList streamList_; + + struct PortStats epochStats_; + +}; + +#endif diff --git a/server/bsdport.cpp b/server/bsdport.cpp new file mode 100644 index 0000000..4ac9ab7 --- /dev/null +++ b/server/bsdport.cpp @@ -0,0 +1,355 @@ +/* +Copyright (C) 2012 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 "bsdport.h" + +#ifdef Q_OS_BSD4 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#define ifr_flagshigh ifr_flags +#define IFF_PPROMISC (IFF_PROMISC << 16) +#endif + +QList BsdPort::allPorts_; +BsdPort::StatsMonitor *BsdPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; + +BsdPort::BsdPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Bsd + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + maxStatsValue_ = ULONG_MAX; +} + +BsdPort::~BsdPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if (ifr.ifr_flagshigh & promisc) + { + ifr.ifr_flagshigh &= ~promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + else + qDebug("Cleared promisc successfully"); + } + else + qDebug("clear_promisc is set but IFF_PPROMISC is not?"); + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void BsdPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +bool BsdPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool BsdPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +BsdPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; +} + +void BsdPort::StatsMonitor::run() +{ + int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; + const int mibLen = sizeof(mib)/sizeof(mib[0]); + QHash portStats; + QHash linkState; + int sd; + QByteArray buf; + size_t len; + char *p, *end; + int count; + struct ifreq ifr; + + // + // We first setup stuff before we start polling for stats + // + if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); + return; + } + + qDebug("sysctl mib returns reqd len = %d\n", (int) len); + len *= 2; // for extra room, just in case! + buf.fill('\0', len); + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); + return; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(sd >= 0); + memset(&ifr, 0, sizeof(ifr)); + + // + // Populate the port stats hash table + // + p = buf.data(); + end = p + len; + count = 0; + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); + + if (ifm->ifm_type == RTM_IFINFO) + { + char ifname[1024]; + + strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + + qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); + foreach(BsdPort* port, allPorts_) + { + if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) + { + Q_ASSERT(ifm->ifm_index == sdl->sdl_index); + portStats[uint(ifm->ifm_index)] = &(port->stats_); + linkState[uint(ifm->ifm_index)] = &(port->linkState_); + + // Set promisc mode, if not already set + strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + short promisc = IFF_PPROMISC >> 16; + + if ((ifr.ifr_flagshigh & promisc) == 0) + { + ifr.ifr_flagshigh |= promisc; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) + { + qDebug("%s: set promisc successful", + port->name()); + port->clearPromisc_ = true; + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + } + else + qDebug("%s: promisc already set", port->name()); + } + else + { + port->isPromisc_ = false; + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + port->name(), strerror(errno)); + } + break; + } + } + count++; + } + p += ifm->ifm_msglen; + } + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in NET_RT_IFLIST - no stats will be available"); + return; + } + + close(sd); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) + { + qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); + goto _try_later; + } + + p = buf.data(); + end = p + len; + + while (p < end) + { + struct if_msghdr *ifm = (struct if_msghdr*) p; + AbstractPort::PortStats *stats; + + if (ifm->ifm_type != RTM_IFINFO) + goto _next; + + stats = portStats[ifm->ifm_index]; + if (stats) + { + struct if_data *ifd = &(ifm->ifm_data); + OstProto::LinkState *state = linkState[ifm->ifm_index]; + u_long in_packets; + + Q_ASSERT(state); +#ifdef Q_OS_MAC + *state = ifm->ifm_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; +#else + *state = (OstProto::LinkState) ifd->ifi_link_state; +#endif + + in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; + stats->rxPps = + ((in_packets >= stats->rxPkts) ? + in_packets - stats->rxPkts : + in_packets + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((ifd->ifi_ibytes >= stats->rxBytes) ? + ifd->ifi_ibytes - stats->rxBytes : + ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = in_packets; + stats->rxBytes = ifd->ifi_ibytes; + stats->txPps = + ((ifd->ifi_opackets >= stats->txPkts) ? + ifd->ifi_opackets - stats->txPkts : + ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((ifd->ifi_obytes >= stats->txBytes) ? + ifd->ifi_obytes - stats->txBytes : + ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = ifd->ifi_opackets; + stats->txBytes = ifd->ifi_obytes; + + stats->rxDrops = ifd->ifi_iqdrops; + stats->rxErrors = ifd->ifi_ierrors; + } +_next: + p += ifm->ifm_msglen; + } +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); +} + +void BsdPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/bsdport.h b/server/bsdport.h new file mode 100644 index 0000000..776c39a --- /dev/null +++ b/server/bsdport.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2012 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 +*/ + +#ifndef _SERVER_BSD_PORT_H +#define _SERVER_BSD_PORT_H + +#include + +#ifdef Q_OS_BSD4 + +#include "pcapport.h" + +class BsdPort : public PcapPort +{ +public: + BsdPort(int id, const char *device); + ~BsdPort(); + + void init(); + + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/drone.cpp b/server/drone.cpp new file mode 100644 index 0000000..c46f1df --- /dev/null +++ b/server/drone.cpp @@ -0,0 +1,53 @@ +/* +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 +*/ + +#include "drone.h" + +#include "rpcserver.h" +#include "myservice.h" + +extern int myport; +extern const char* version; +extern const char* revision; + +Drone::Drone(QObject *parent) + : QObject(parent) +{ + rpcServer = new RpcServer(); + service = new MyService(); +} + +Drone::~Drone() +{ + delete rpcServer; + delete service; +} + +bool Drone::init() +{ + Q_ASSERT(rpcServer); + + if (!rpcServer->registerService(service, myport ? myport : 7878)) + { + //qCritical(qPrintable(rpcServer->errorString())); + return false; + } + + return true; +} diff --git a/server/drone.h b/server/drone.h new file mode 100644 index 0000000..9474207 --- /dev/null +++ b/server/drone.h @@ -0,0 +1,40 @@ +/* +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 +*/ + +#ifndef _DRONE_H +#define _DRONE_H + +#include + +class RpcServer; +namespace OstProto { class OstService; } + +class Drone : public QObject +{ + Q_OBJECT +public: + Drone(QObject *parent = 0); + ~Drone(); + bool init(); + +private: + RpcServer *rpcServer; + OstProto::OstService *service; +}; +#endif diff --git a/server/drone.pro b/server/drone.pro new file mode 100644 index 0000000..d22f138 --- /dev/null +++ b/server/drone.pro @@ -0,0 +1,49 @@ +TEMPLATE = app +CONFIG += qt ver_info +QT += network script +QT -= gui +DEFINES += HAVE_REMOTE WPCAP +linux*:system(grep -q IFLA_STATS64 /usr/include/linux/if_link.h): \ + DEFINES += HAVE_IFLA_STATS64 +INCLUDEPATH += "../rpc" +win32 { + CONFIG += console + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" +} +LIBS += -lm +LIBS += -lprotobuf +HEADERS += drone.h +SOURCES += \ + drone_main.cpp \ + drone.cpp \ + portmanager.cpp \ + abstractport.cpp \ + pcapport.cpp \ + bsdport.cpp \ + linuxport.cpp \ + winpcapport.cpp +SOURCES += myservice.cpp +SOURCES += pcapextra.cpp + +QMAKE_DISTCLEAN += object_script.* + +include (../install.pri) +include (../version.pri) diff --git a/server/drone_main.cpp b/server/drone_main.cpp new file mode 100644 index 0000000..e5c1abd --- /dev/null +++ b/server/drone_main.cpp @@ -0,0 +1,107 @@ +/* +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 +*/ + +#include "drone.h" + +#include "../common/protocolmanager.h" +#include "settings.h" + +#include + +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif + +extern ProtocolManager *OstProtocolManager; +extern char *version; +extern char *revision; + +QSettings *appSettings; +int myport; + +void cleanup(int /*signum*/) +{ + QCoreApplication::instance()->exit(-1); +} + +int main(int argc, char *argv[]) +{ + int exitCode = 0; + QCoreApplication app(argc, argv); + Drone *drone; + + // TODO: command line options + // -v (--version) + // -h (--help) + // -p (--portnum) + if (argc > 1) + myport = atoi(argv[1]); + + app.setApplicationName("Drone"); + app.setOrganizationName("Ostinato"); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/drone.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(QSettings::IniFormat, + QSettings::UserScope, + app.organizationName(), + app.applicationName().toLower()); + + drone = new Drone(); + OstProtocolManager = new ProtocolManager(); + + if (!drone->init()) + { + exitCode = -1; + goto _exit; + } + + qDebug("Version: %s", version); + qDebug("Revision: %s", revision); + +#ifdef Q_OS_UNIX + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup; + if (sigaction(SIGTERM, &sa, NULL)) + qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); + if (sigaction(SIGINT, &sa, NULL)) + qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); +#endif + + exitCode = app.exec(); + +_exit: + delete drone; + delete OstProtocolManager; + + google::protobuf::ShutdownProtobufLibrary(); + + return exitCode; +} + diff --git a/server/icons/portgroup.png b/server/icons/portgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc37dce369d66bdf38393b191df4d7e6c7ccd54 GIT binary patch literal 667 zcmV;M0%ZM(P)a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu( +*/ + +#include "linuxport.h" + +#ifdef Q_OS_LINUX + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QList LinuxPort::allPorts_; +LinuxPort::StatsMonitor *LinuxPort::monitor_; + +const quint32 kMaxValue32 = 0xffffffff; +const quint64 kMaxValue64 = 0xffffffffffffffffULL; + +#ifdef HAVE_IFLA_STATS64 +#define X_IFLA_STATS IFLA_STATS64 +typedef struct rtnl_link_stats64 x_rtnl_link_stats; +#else +#define X_IFLA_STATS IFLA_STATS +typedef struct rtnl_link_stats x_rtnl_link_stats; +#endif + +LinuxPort::LinuxPort(int id, const char *device) + : PcapPort(id, device) +{ + isPromisc_ = true; + clearPromisc_ = false; + + // We don't need per port Rx/Tx monitors for Linux + delete monitorRx_; + delete monitorTx_; + monitorRx_ = monitorTx_ = NULL; + + // We have one monitor for both Rx/Tx of all ports + if (!monitor_) + monitor_ = new StatsMonitor(); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 16; + + qDebug("adding dev to all ports list <%s>", device); + allPorts_.append(this); + + // A port can support either 32 or 64 bit stats - we will attempt + // to guess this for each port and initialize this variable at + // run time when the counter wraps around + maxStatsValue_ = 0; +} + +LinuxPort::~LinuxPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitor_->isRunning()) + { + monitor_->stop(); + monitor_->wait(); + } + + if (clearPromisc_) + { + int sd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); + + if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) + { + if (ifr.ifr_flags & IFF_PROMISC) + { + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) + qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", + strerror(errno)); + } + } + else + qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", + strerror(errno)); + + close(sd); + } +} + +void LinuxPort::init() +{ + if (!monitor_->isRunning()) + monitor_->start(); + + monitor_->waitForSetupFinished(); + + if (!isPromisc_) + addNote("Non Promiscuous Mode"); +} + +OstProto::LinkState LinuxPort::linkState() +{ + return linkState_; +} + +bool LinuxPort::hasExclusiveControl() +{ + // TODO + return false; +} + +bool LinuxPort::setExclusiveControl(bool /*exclusive*/) +{ + // TODO + return false; +} + +LinuxPort::StatsMonitor::StatsMonitor() + : QThread() +{ + stop_ = false; + setupDone_ = false; + ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); + Q_ASSERT(ioctlSocket_ >= 0); +} + +LinuxPort::StatsMonitor::~StatsMonitor() +{ + close(ioctlSocket_); +} + +void LinuxPort::StatsMonitor::run() +{ + if (netlinkStats() < 0) + { + qDebug("netlink stats not available - using /proc stats"); + procStats(); + } +} + +void LinuxPort::StatsMonitor::procStats() +{ + PortStats **portStats; + int fd; + QByteArray buf; + int len; + char *p, *end; + int count, index; + const char* fmtopt[] = { + "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", + "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", + }; + const char *fmt; + + // + // We first setup stuff before we start polling for stats + // + fd = open("/proc/net/dev", O_RDONLY); + if (fd < 0) + { + qWarning("Unable to open /proc/net/dev - no stats will be available"); + return; + } + + buf.fill('\0', 8192); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + qWarning("initial buffer size is too small. no stats will be available"); + return; + } + + p = buf.data(); + end = p + len; + + // Select scanf format + if (strstr(buf, "compressed")) + fmt = fmtopt[0]; + else + fmt = fmtopt[1]; + + // Count number of lines - number of ports is 2 less than number of lines + count = 0; + while (p < end) + { + if (*p == '\n') + count++; + p++; + } + count -= 2; + + if (count <= 0) + { + qWarning("no ports in /proc/dev/net - no stats will be available"); + return; + } + + portStats = (PortStats**) calloc(count, sizeof(PortStats)); + Q_ASSERT(portStats != NULL); + + // + // Populate the port stats array + // + p = buf.data(); + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + char* q; + + // Skip whitespace + while ((p < end) && (*p == ' ')) + p++; + + q = p; + + // Get interface name + while ((q < end) && (*q != ':') && (*q != '\n')) + q++; + + if ((q < end) && (*q == ':')) + { + foreach(LinuxPort* port, allPorts_) + { + if (strncmp(port->name(), p, int(q-p)) == 0) + { + portStats[index] = &(port->stats_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + break; + } + } + } + index++; + + // Skip till newline + p = q; + while (*p != '\n') + p++; + p++; + } + Q_ASSERT(index == count); + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + lseek(fd, 0, SEEK_SET); + len = read(fd, (void*) buf.data(), buf.size()); + if (len < 0) + { + if (buf.size() > 1*1024*1024) + { + qWarning("buffer size hit limit. no more stats"); + return; + } + qDebug("doubling buffer size. curr = %d", buf.size()); + buf.resize(buf.size() * 2); + continue; + } + + p = buf.data(); + end = p + len; + + // Skip first two lines + while (*p != '\n') + p++; + p++; + while (*p != '\n') + p++; + p++; + + index = 0; + while (p < end) + { + uint dummy; + quint64 rxBytes, rxPkts; + quint64 rxErrors, rxDrops, rxFifo, rxFrame; + quint64 txBytes, txPkts; + + // Skip interface name - we assume the number and order of ports + // won't change since we parsed the output before we started polling + while ((p < end) && (*p != ':') && (*p != '\n')) + p++; + if (p >= end) + break; + if (*p == '\n') + { + index++; + continue; + } + p++; + + sscanf(p, fmt, + &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, + &dummy, &dummy, + &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy); + + if (index < count) + { + AbstractPort::PortStats *stats = portStats[index]; + if (stats) + { + // TODO: fix the pps/Bps calc similar to netlink stats + stats->rxPps = + ((rxPkts >= stats->rxPkts) ? + rxPkts - stats->rxPkts : + rxPkts + (kMaxValue32 - stats->rxPkts)) + / kRefreshFreq_; + stats->rxBps = + ((rxBytes >= stats->rxBytes) ? + rxBytes - stats->rxBytes : + rxBytes + (kMaxValue32 - stats->rxBytes)) + / kRefreshFreq_; + stats->rxPkts = rxPkts; + stats->rxBytes = rxBytes; + stats->txPps = + ((txPkts >= stats->txPkts) ? + txPkts - stats->txPkts : + txPkts + (kMaxValue32 - stats->txPkts)) + / kRefreshFreq_; + stats->txBps = + ((txBytes >= stats->txBytes) ? + txBytes - stats->txBytes : + txBytes + (kMaxValue32 - stats->txBytes)) + / kRefreshFreq_; + stats->txPkts = txPkts; + stats->txBytes = txBytes; + + stats->rxDrops = rxDrops; + stats->rxErrors = rxErrors; + stats->rxFifoErrors = rxFifo; + stats->rxFrameErrors = rxFrame; + } + } + + while (*p != '\n') + p++; + p++; + index++; + } + QThread::sleep(kRefreshFreq_); + } + + free(portStats); +} + +int LinuxPort::StatsMonitor::netlinkStats() +{ + QHash portStats; + QHash portMaxStatsValue; + QHash linkState; + int fd; + struct sockaddr_nl local; + struct sockaddr_nl kernel; + QByteArray buf; + int len, count; + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtg; + } ifListReq; + struct iovec iov; + struct msghdr msg; + struct nlmsghdr *nlm; + bool done = false; + + // + // We first setup stuff before we start polling for stats + // + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + qWarning("Unable to open netlink socket (errno %d)", errno); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) + { + qWarning("Unable to bind netlink socket (errno %d)", errno); + return -1; + } + + memset(&ifListReq, 0, sizeof(ifListReq)); + ifListReq.nlh.nlmsg_len = sizeof(ifListReq); + ifListReq.nlh.nlmsg_type = RTM_GETLINK; + ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + ifListReq.nlh.nlmsg_pid = 0; + ifListReq.rtg.rtgen_family = AF_PACKET; + + buf.fill('\0', 1024); + + msg.msg_name = &kernel; + msg.msg_namelen = sizeof(kernel); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); + + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + return -1; + } + + count = 0; + +_retry: + + // Find required size of buffer and resize accordingly + while (1) + { + iov.iov_base = buf.data(); + iov.iov_len = buf.size(); + msg.msg_flags = 0; + + // Peek at reply to check buffer size required + len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink closed the socket on my face!"); + return -1; + } + else + { + if (msg.msg_flags & MSG_TRUNC) + { + if (len == buf.size()) // Older Kernel returns truncated size + { + qDebug("netlink buffer size %d not enough", buf.size()); + qDebug("retrying with double the size"); + // Double the size and retry + buf.resize(buf.size()*2); + continue; + } + else // Newer Kernel returns actual size required + { + qDebug("netlink required buffer size = %d", len); + buf.resize(len); + continue; + } + } + else + qDebug("buffer size %d enough for netlink", buf.size()); + + break; + } + } + + msg.msg_flags = 0; + + // Actually receive the reply now + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry; + qWarning("netlink recv error %d", errno); + return -1; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + return -1; + } + + // + // Populate the port stats hash table + // + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + char ifname[64] = ""; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error %d", err->error); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == IFLA_IFNAME) + { + strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); + ifname[RTA_PAYLOAD(rta)] = 0; + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + + qDebug("if: %s(%d)", ifname, ifi->ifi_index); + foreach(LinuxPort* port, allPorts_) + { + if (strcmp(port->name(), ifname) == 0) + { + portStats[uint(ifi->ifi_index)] = &(port->stats_); + portMaxStatsValue[uint(ifi->ifi_index)] = + &(port->maxStatsValue_); + linkState[uint(ifi->ifi_index)] = &(port->linkState_); + + if (setPromisc(port->name())) + port->clearPromisc_ = true; + else + port->isPromisc_ = false; + + count++; + break; + } + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry; + + qDebug("port count = %d\n", count); + if (count <= 0) + { + qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); + return - 1; + } + + qDebug("stats for %d ports setup", count); + setupDone_ = true; + + // + // We are all set - Let's start polling for stats! + // + while (!stop_) + { + if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) + { + qWarning("Unable to send GETLINK request (errno %d)", errno); + goto _try_later; + } + + done = false; + +_retry_recv: + msg.msg_flags = 0; + len = recvmsg(fd, &msg, 0); + + if (len < 0) + { + if (errno == EINTR || errno == EAGAIN) + goto _retry_recv; + qWarning("netlink recv error %d", errno); + break; + } + else if (len == 0) + { + qWarning("netlink socket closed unexpectedly"); + break; + } + + nlm = (struct nlmsghdr*) buf.data(); + while (NLMSG_OK(nlm, (uint)len)) + { + struct ifinfomsg *ifi; + struct rtattr *rta; + int rtaLen; + + if (nlm->nlmsg_type == NLMSG_DONE) + { + done = true; + break; + } + + if (nlm->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); + qDebug("RTNETLINK error: %s", strerror(-err->error)); + done = true; + break; + } + + Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); + + ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); + rta = IFLA_RTA(ifi); + rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); + while (RTA_OK(rta, rtaLen)) + { + if (rta->rta_type == X_IFLA_STATS) + { + x_rtnl_link_stats *rtnlStats = + (x_rtnl_link_stats*) RTA_DATA(rta); + AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; + quint64 *maxStatsValue = portMaxStatsValue[ifi->ifi_index]; + OstProto::LinkState *state = linkState[ifi->ifi_index]; + + if (!stats) + break; + + if (rtnlStats->rx_packets >= stats->rxPkts) { + stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->rxPkts > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->rxPps = ((*maxStatsValue - stats->rxPkts) + + rtnlStats->rx_packets) + / kRefreshFreq_; + } + + if (rtnlStats->rx_bytes >= stats->rxBytes) { + stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->rxBytes > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->rxBps = ((*maxStatsValue - stats->rxBytes) + + rtnlStats->rx_bytes) + / kRefreshFreq_; + } + + stats->rxPkts = rtnlStats->rx_packets; + stats->rxBytes = rtnlStats->rx_bytes; + + if (rtnlStats->tx_packets >= stats->txPkts) { + stats->txPps = (rtnlStats->tx_packets - stats->txPkts) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->txPkts > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->txPps = ((*maxStatsValue - stats->txPkts) + + rtnlStats->tx_packets) + / kRefreshFreq_; + } + + if (rtnlStats->tx_bytes >= stats->txBytes) { + stats->txBps = (rtnlStats->tx_bytes - stats->txBytes) + / kRefreshFreq_; + } + else { + if (*maxStatsValue == 0) { + *maxStatsValue = stats->txBytes > kMaxValue32 ? + kMaxValue64 : kMaxValue32; + } + stats->txBps = ((*maxStatsValue - stats->txBytes) + + rtnlStats->tx_bytes) + / kRefreshFreq_; + } + + stats->txPkts = rtnlStats->tx_packets; + stats->txBytes = rtnlStats->tx_bytes; + + // TODO: export detailed error stats + stats->rxDrops = rtnlStats->rx_dropped + + rtnlStats->rx_missed_errors; + stats->rxErrors = rtnlStats->rx_errors; + stats->rxFifoErrors = rtnlStats->rx_fifo_errors; + stats->rxFrameErrors = rtnlStats->rx_crc_errors + + rtnlStats->rx_length_errors + + rtnlStats->rx_over_errors + + rtnlStats->rx_frame_errors; + + Q_ASSERT(state); + *state = ifi->ifi_flags & IFF_RUNNING ? + OstProto::LinkStateUp : OstProto::LinkStateDown; + + break; + } + rta = RTA_NEXT(rta, rtaLen); + } + nlm = NLMSG_NEXT(nlm, len); + } + + if (!done) + goto _retry_recv; + +_try_later: + QThread::sleep(kRefreshFreq_); + } + + portStats.clear(); + linkState.clear(); + + return 0; +} + +int LinuxPort::StatsMonitor::setPromisc(const char * portName) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); + + if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) + { + if ((ifr.ifr_flags & IFF_PROMISC) == 0) + { + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) + { + return 1; + } + else + { + qDebug("%s: failed to set promisc; " + "SIOCSIFFLAGS failed (%s)", + portName, strerror(errno)); + } + } + } + else + { + qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", + portName, strerror(errno)); + } + + return 0; +} + +void LinuxPort::StatsMonitor::stop() +{ + stop_ = true; +} + +bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) +{ + QTime t; + + t.start(); + while (!setupDone_) + { + if (t.elapsed() > msecs) + return false; + + QThread::msleep(10); + } + + return true; +} +#endif diff --git a/server/linuxport.h b/server/linuxport.h new file mode 100644 index 0000000..2658560 --- /dev/null +++ b/server/linuxport.h @@ -0,0 +1,68 @@ +/* +Copyright (C) 2011 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 +*/ + +#ifndef _SERVER_LINUX_PORT_H +#define _SERVER_LINUX_PORT_H + +#include + +#ifdef Q_OS_LINUX + +#include "pcapport.h" + +class LinuxPort : public PcapPort +{ +public: + LinuxPort(int id, const char *device); + ~LinuxPort(); + + void init(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class StatsMonitor: public QThread + { + public: + StatsMonitor(); + ~StatsMonitor(); + void run(); + void stop(); + bool waitForSetupFinished(int msecs = 10000); + private: + int netlinkStats(); + void procStats(); + int setPromisc(const char* portName); + + static const int kRefreshFreq_ = 1; // in seconds + bool stop_; + bool setupDone_; + int ioctlSocket_; + }; + + bool isPromisc_; + bool clearPromisc_; + static QList allPorts_; + static StatsMonitor *monitor_; // rx/tx stats for ALL ports +}; +#endif + +#endif diff --git a/server/myservice.cpp b/server/myservice.cpp new file mode 100644 index 0000000..55f3bf3 --- /dev/null +++ b/server/myservice.cpp @@ -0,0 +1,581 @@ +/* +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 +*/ + + +#include "myservice.h" + +#if 0 +#include +#include +#include "qdebug.h" + +#include "../common/protocollistiterator.h" +#include "../common/abstractprotocol.h" +#endif + +#include "../common/streambase.h" +#include "../rpc/pbrpccontroller.h" +#include "portmanager.h" + +#include + + +extern char *version; + +MyService::MyService() +{ + PortManager *portManager = PortManager::instance(); + int n = portManager->portCount(); + + for (int i = 0; i < n; i++) { + portInfo.append(portManager->port(i)); + portLock.append(new QReadWriteLock()); + } +} + +MyService::~MyService() +{ + while (!portLock.isEmpty()) + delete portLock.takeFirst(); + //! \todo Use a singleton destroyer instead + // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt + delete PortManager::instance(); +} + +void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::Void* /*request*/, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + // No locks are needed here because the list does not change + // and neither does the port_id + + for (int i = 0; i < portInfo.size(); i++) + { + ::OstProto::PortId *p; + + p = response->add_port_id(); + p->set_id(portInfo[i]->id()); + } + + done->Run(); +} + +void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int id; + + id = request->port_id(i).id(); + if (id < portInfo.size()) + { + OstProto::Port *p; + + p = response->add_port(); + portLock[id]->lockForRead(); + portInfo[id]->protoDataCopyInto(p); + portLock[id]->unlock(); + } + } + + done->Run(); +} + +void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_size(); i++) + { + OstProto::Port port; + int id; + + port = request->port(i); + id = port.port_id().id(); + if (id < portInfo.size()) + { + portLock[id]->lockForWrite(); + portInfo[id]->modify(port); + portLock[id]->unlock(); + } + } + + //! \todo (LOW): fill-in response "Ack"???? + done->Run(); +} + +void MyService::getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + portLock[portId]->lockForRead(); + for (int i = 0; i < portInfo[portId]->streamCount(); i++) + { + OstProto::StreamId *s; + + s = response->add_stream_id(); + s->set_id(portInfo[portId]->streamAtIndex(i)->id()); + } + portLock[portId]->unlock(); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("Invalid Port Id"); + done->Run(); +} + +void MyService::getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + response->mutable_port_id()->set_id(portId); + portLock[portId]->lockForRead(); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + OstProto::Stream *s; + + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (!stream) + continue; //! \todo(LOW): Partial status of RPC + + s = response->add_stream(); + stream->protoDataCopyInto(*s); + } + portLock[portId]->unlock(); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + + portLock[portId]->lockForWrite(); + for (int i = 0; i < request->stream_id_size(); i++) + { + StreamBase *stream; + + // If stream with same id as in request exists already ==> error!! + stream = portInfo[portId]->stream(request->stream_id(i).id()); + if (stream) + continue; //! \todo (LOW): Partial status of RPC + + // Append a new "default" stream - actual contents of the new stream is + // expected in a subsequent "modifyStream" request - set the stream id + // now itself however!!! + stream = new StreamBase; + stream->setId(request->stream_id(i).id()); + portInfo[portId]->addStream(stream); + } + portLock[portId]->unlock(); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; + +_invalid_port: + controller->SetFailed("invalid portid"); +_exit: + done->Run(); +} + +void MyService::deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + + portLock[portId]->lockForWrite(); + for (int i = 0; i < request->stream_id_size(); i++) + portInfo[portId]->deleteStream(request->stream_id(i).id()); + portLock[portId]->unlock(); + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; +_invalid_port: + controller->SetFailed("invalid portid"); +_exit: + done->Run(); +} + +void MyService::modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->port_id().id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + if (portInfo[portId]->isTransmitOn()) + goto _port_busy; + + portLock[portId]->lockForWrite(); + for (int i = 0; i < request->stream_size(); i++) + { + StreamBase *stream; + + stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); + if (stream) + { + stream->protoDataCopyFrom(request->stream(i)); + portInfo[portId]->setDirty(); + } + } + + if (portInfo[portId]->isDirty()) + portInfo[portId]->updatePacketList(); + portLock[portId]->unlock(); + + //! \todo(LOW): fill-in response "Ack"???? + + done->Run(); + return; + +_port_busy: + controller->SetFailed("Port Busy"); + goto _exit; +_invalid_port: + controller->SetFailed("invalid portid"); +_exit: + done->Run(); +} + +void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portLock[portId]->lockForWrite(); + portInfo[portId]->startTransmit(); + portLock[portId]->unlock(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopTransmit(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portLock[portId]->lockForWrite(); + portInfo[portId]->stopTransmit(); + portLock[portId]->unlock(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portLock[portId]->lockForWrite(); + portInfo[portId]->startCapture(); + portLock[portId]->unlock(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + for (int i=0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portLock[portId]->lockForWrite(); + portInfo[portId]->stopCapture(); + portLock[portId]->unlock(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* /*response*/, + ::google::protobuf::Closure* done) +{ + int portId; + + qDebug("In %s", __PRETTY_FUNCTION__); + + portId = request->id(); + if ((portId < 0) || (portId >= portInfo.size())) + goto _invalid_port; + + portLock[portId]->lockForWrite(); + portInfo[portId]->stopCapture(); + static_cast(controller)->setBinaryBlob( + portInfo[portId]->captureData()); + portLock[portId]->unlock(); + + done->Run(); + return; + +_invalid_port: + controller->SetFailed("invalid portid"); + done->Run(); +} + +void MyService::getStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done) +{ + //qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + AbstractPort::PortStats stats; + OstProto::PortStats *s; + OstProto::PortState *st; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo(LOW): partial rpc? + + s = response->add_port_stats(); + s->mutable_port_id()->set_id(request->port_id(i).id()); + + st = s->mutable_state(); + portLock[portId]->lockForRead(); + st->set_link_state(portInfo[portId]->linkState()); + st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); + st->set_is_capture_on(portInfo[portId]->isCaptureOn()); + + portInfo[portId]->stats(&stats); + portLock[portId]->unlock(); + +#if 0 + if (portId == 2) + qDebug(">%llu", stats.rxPkts); +#endif + + s->set_rx_pkts(stats.rxPkts); + s->set_rx_bytes(stats.rxBytes); + s->set_rx_pps(stats.rxPps); + s->set_rx_bps(stats.rxBps); + + s->set_tx_pkts(stats.txPkts); + s->set_tx_bytes(stats.txBytes); + s->set_tx_pps(stats.txPps); + s->set_tx_bps(stats.txBps); + + s->set_rx_drops(stats.rxDrops); + s->set_rx_errors(stats.rxErrors); + s->set_rx_fifo_errors(stats.rxFifoErrors); + s->set_rx_frame_errors(stats.rxFrameErrors); + } + + done->Run(); +} + +void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* /*response*/, + ::google::protobuf::Closure* done) +{ + qDebug("In %s", __PRETTY_FUNCTION__); + + for (int i = 0; i < request->port_id_size(); i++) + { + int portId; + + portId = request->port_id(i).id(); + if ((portId < 0) || (portId >= portInfo.size())) + continue; //! \todo (LOW): partial RPC? + + portLock[portId]->lockForWrite(); + portInfo[portId]->resetStats(); + portLock[portId]->unlock(); + } + + //! \todo (LOW): fill-in response "Ack"???? + + done->Run(); +} + +void MyService::checkVersion(::google::protobuf::RpcController* controller, + const ::OstProto::VersionInfo* request, + ::OstProto::VersionCompatibility* response, + ::google::protobuf::Closure* done) +{ + QString myVersion(version); + QString clientVersion; + QStringList my, client; + + qDebug("In %s", __PRETTY_FUNCTION__); + + my = myVersion.split('.'); + + Q_ASSERT(my.size() >= 2); + + clientVersion = QString::fromStdString(request->version()); + client = clientVersion.split('.'); + + qDebug("client = %s, my = %s", + qPrintable(clientVersion), qPrintable(myVersion)); + + if (client.size() < 2) + goto _invalid_version; + + // Compare only major and minor numbers + if (client[0] == my[0] && client[1] == my[1]) { + response->set_result(OstProto::VersionCompatibility::kCompatible); + } + else { + response->set_result(OstProto::VersionCompatibility::kIncompatible); + response->set_notes(QString("Drone needs client version %1.%2.x") + .arg(my[0], my[1]).toStdString()); + static_cast(controller)->TriggerDisconnect(); + } + + done->Run(); + return; + +_invalid_version: + controller->SetFailed("invalid version information"); + done->Run(); +} diff --git a/server/myservice.h b/server/myservice.h new file mode 100644 index 0000000..15c2f5f --- /dev/null +++ b/server/myservice.h @@ -0,0 +1,121 @@ +/* +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 +*/ + +#ifndef _MY_SERVICE_H +#define _MY_SERVICE_H + +#include "../common/protocol.pb.h" + +#include +#include + +#define MAX_PKT_HDR_SIZE 1536 +#define MAX_STREAM_NAME_SIZE 64 + +class AbstractPort; + +class MyService: public OstProto::OstService +{ +public: + MyService(); + virtual ~MyService(); + + /* Methods provided by the service */ + virtual void getPortIdList(::google::protobuf::RpcController* controller, + const ::OstProto::Void* request, + ::OstProto::PortIdList* response, + ::google::protobuf::Closure* done); + virtual void getPortConfig(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortConfigList* response, + ::google::protobuf::Closure* done); + virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, + const ::OstProto::PortConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getStreamIdList(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::StreamIdList* response, + ::google::protobuf::Closure* done); + virtual void getStreamConfig(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::StreamConfigList* response, + ::google::protobuf::Closure* done); + virtual void addStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void deleteStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void modifyStream(::google::protobuf::RpcController* controller, + const ::OstProto::StreamConfigList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startTransmit(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopTransmit(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void startCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void stopCapture(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, + const ::OstProto::PortId* request, + ::OstProto::CaptureBuffer* response, + ::google::protobuf::Closure* done); + virtual void getStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::PortStatsList* response, + ::google::protobuf::Closure* done); + virtual void clearStats(::google::protobuf::RpcController* controller, + const ::OstProto::PortIdList* request, + ::OstProto::Ack* response, + ::google::protobuf::Closure* done); + virtual void checkVersion(::google::protobuf::RpcController* controller, + const ::OstProto::VersionInfo* request, + ::OstProto::VersionCompatibility* response, + ::google::protobuf::Closure* done); + +private: + /* + * NOTES: + * - AbstractPort::id() and index into portInfo[] are same! + * - portLock[] size and order should be same as portInfo[] as the + * same index is used for both. + * - we assume that once populated by the constructor, the list(s) + * never change (objects in the list can change, but not the list itself) + * - locking is at port granularity, not at stream granularity - for now + * this seems sufficient. Revisit later, if required + */ + QList portInfo; + QList portLock; + +}; + +#endif diff --git a/server/pcapextra.cpp b/server/pcapextra.cpp new file mode 100644 index 0000000..4acbda9 --- /dev/null +++ b/server/pcapextra.cpp @@ -0,0 +1,78 @@ +/* +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 +*/ + +#include "pcapextra.h" + +#include // memcpy() +#include // malloc(), free() + +/* NOTE: All code borrowed from WinPcap */ + +#ifndef Q_OS_WIN32 +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) +{ + pcap_send_queue *tqueue; + + /* Allocate the queue */ + tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); + if(tqueue == NULL){ + return NULL; + } + + /* Allocate the buffer */ + tqueue->buffer = (char*)malloc(memsize); + if(tqueue->buffer == NULL){ + free(tqueue); + return NULL; + } + + tqueue->maxlen = memsize; + tqueue->len = 0; + + return tqueue; +} + +void pcap_sendqueue_destroy (pcap_send_queue *queue) +{ + free(queue->buffer); + free(queue); +} + +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +{ + if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > + queue->maxlen) + { + return -1; + } + + /* Copy the pcap_pkthdr header*/ + memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); + queue->len += sizeof(struct pcap_pkthdr); + + /* copy the packet */ + memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); + queue->len += pkt_header->caplen; + + return 0; +} +#endif + + diff --git a/server/pcapextra.h b/server/pcapextra.h new file mode 100644 index 0000000..415fe3e --- /dev/null +++ b/server/pcapextra.h @@ -0,0 +1,45 @@ +/* +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 +*/ + +#ifndef _PCAP_EXTRA_H +#define _PCAP_EXTRA_H + +#include +#include + +#ifndef Q_OS_WIN32 + +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +struct pcap_send_queue +{ + u_int maxlen; + u_int len; + char *buffer; +}; + +pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); +void pcap_sendqueue_destroy (pcap_send_queue *queue); +int pcap_sendqueue_queue (pcap_send_queue *queue, + const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +#endif + +#endif + diff --git a/server/pcapport.cpp b/server/pcapport.cpp new file mode 100644 index 0000000..c46d5ad --- /dev/null +++ b/server/pcapport.cpp @@ -0,0 +1,857 @@ +/* +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 +*/ + +#include "pcapport.h" + +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +pcap_if_t *PcapPort::deviceList_ = NULL; + + +#if defined(Q_OS_LINUX) +typedef struct timeval TimeStamp; +static void inline getTimeStamp(TimeStamp *stamp) +{ + gettimeofday(stamp, NULL); +} + +// Returns time diff in usecs between end and start +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + struct timeval diff; + long usecs; + + timersub(end, start, &diff); + + usecs = diff.tv_usec; + if (diff.tv_sec) + usecs += diff.tv_sec*1e6; + + return usecs; +} +#elif defined(Q_OS_WIN32) +static quint64 gTicksFreq; +typedef LARGE_INTEGER TimeStamp; +static void inline getTimeStamp(TimeStamp* stamp) +{ + QueryPerformanceCounter(stamp); +} + +static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) +{ + if (end->QuadPart >= start->QuadPart) + return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; + else + { + // FIXME: incorrect! what's the max value for this counter before + // it rolls over? + return (start->QuadPart)*long(1e6)/gTicksFreq; + } +} +#else +typedef int TimeStamp; +static void inline getTimeStamp(TimeStamp*) {} +static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } +#endif + +PcapPort::PcapPort(int id, const char *device) + : AbstractPort(id, device) +{ + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + transmitter_ = new PortTransmitter(device); + capturer_ = new PortCapturer(device); + + if (!monitorRx_->handle() || !monitorTx_->handle()) + isUsable_ = false; + + if (!deviceList_) + { + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&deviceList_, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + } + + for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) + { + if (strcmp(device, dev->name) == 0) + { +#ifdef Q_OS_WIN32 + data_.set_name(QString("if%1").arg(id).toStdString()); +#else + if (dev->name) + data_.set_name(dev->name); +#endif + if (dev->description) + data_.set_description(dev->description); + + //! \todo set port IP addr also + } + } +} + +void PcapPort::init() +{ + if (!monitorTx_->isDirectional()) + transmitter_->useExternalStats(&stats_); + + transmitter_->setHandle(monitorRx_->handle()); + + updateNotes(); + + monitorRx_->start(); + monitorTx_->start(); +} + +PcapPort::~PcapPort() +{ + qDebug("In %s", __FUNCTION__); + + if (monitorRx_) + monitorRx_->stop(); + if (monitorTx_) + monitorTx_->stop(); + + delete capturer_; + delete transmitter_; + + if (monitorRx_) + monitorRx_->wait(); + delete monitorRx_; + + if (monitorTx_) + monitorTx_->wait(); + delete monitorTx_; +} + +void PcapPort::updateNotes() +{ + QString notes; + + if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) + notes.append("
  • Non Promiscuous Mode
  • "); + + if (!monitorRx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); + + if (!monitorTx_->isDirectional() && !hasExclusiveControl()) + notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); + + if (notes.isEmpty()) + data_.set_notes(""); + else + data_.set_notes(QString("Limitation(s)" + "
      %1
    " + "Rx/Tx Rates are also subject to above limitation(s)"). + arg(notes).toStdString()); +} + +PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) +{ + int ret; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + bool noLocalCapture; + + direction_ = direction; + isDirectional_ = true; + isPromisc_ = true; + noLocalCapture = true; + stats_ = stats; + stop_ = false; + +_retry: +#ifdef Q_OS_WIN32 + int flags = 0; + + if (isPromisc_) + flags |= PCAP_OPENFLAG_PROMISCUOUS; + if (noLocalCapture) + flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; + + handle_ = pcap_open(device, 64 /* FIXME */, flags, + 1000 /* ms */, NULL, errbuf); +#else + handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), + 1000 /* ms */, errbuf); +#endif + + if (handle_ == NULL) + { + if (isPromisc_ && QString(errbuf).contains("promiscuous")) + { + qDebug("Can't set promiscuous mode, trying non-promisc %s", device); + isPromisc_ = false; + goto _retry; + } + else if (noLocalCapture && QString(errbuf).contains("loopback")) + { + qDebug("Can't set no local capture mode %s", device); + noLocalCapture = false; + goto _retry; + } + else + goto _open_error; + } +#ifdef Q_OS_WIN32 + // pcap_setdirection() API is not supported in Windows. + // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 + // but since we would like to work with previous versions of WinPcap + // also, we assume the API does not exist + ret = -1; +#else + switch (direction_) + { + case kDirectionRx: + ret = pcap_setdirection(handle_, PCAP_D_IN); + break; + case kDirectionTx: + ret = pcap_setdirection(handle_, PCAP_D_OUT); + break; + default: + ret = -1; // avoid 'may be used uninitialized' warning + Q_ASSERT(false); + } +#endif + + if (ret < 0) + goto _set_direction_error; + + return; + +_set_direction_error: + qDebug("Error setting direction(%d) %s: %s\n", direction, device, + pcap_geterr(handle_)); + isDirectional_ = false; + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); +} + +PcapPort::PortMonitor::~PortMonitor() +{ + if (handle_) + pcap_close(handle_); +} + +void PcapPort::PortMonitor::run() +{ + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + switch (direction_) + { + case kDirectionRx: + stats_->rxPkts++; + stats_->rxBytes += hdr->len; + break; + + case kDirectionTx: + if (isDirectional_) + { + stats_->txPkts++; + stats_->txBytes += hdr->len; + } + break; + + default: + Q_ASSERT(false); + } + + //! \todo TODO pkt/bit rates + break; + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + } +} + +void PcapPort::PortMonitor::stop() +{ + stop_ = true; + pcap_breakloop(handle()); +} + +PcapPort::PortTransmitter::PortTransmitter(const char *device) +{ + char errbuf[PCAP_ERRBUF_SIZE] = ""; + +#ifdef Q_OS_WIN32 + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq)) + gTicksFreq = ticksFreq_ = freq.QuadPart; + else + Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", + "This Win32 platform does not support performance counter"); +#endif + state_ = kNotStarted; + returnToQIdx_ = -1; + loopDelay_ = 0; + stop_ = false; + stats_ = new AbstractPort::PortStats; + usingInternalStats_ = true; + handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + goto _open_error; + + usingInternalHandle_ = true; + + return; + +_open_error: + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); + usingInternalHandle_ = false; +} + +PcapPort::PortTransmitter::~PortTransmitter() +{ + if (usingInternalStats_) + delete stats_; + if (usingInternalHandle_) + pcap_close(handle_); +} + +void PcapPort::PortTransmitter::clearPacketList() +{ + Q_ASSERT(!isRunning()); + // \todo lock for packetSequenceList + while(packetSequenceList_.size()) + delete packetSequenceList_.takeFirst(); + + currentPacketSequence_ = NULL; + repeatSequenceStart_ = -1; + repeatSize_ = 0; + packetCount_ = 0; + + returnToQIdx_ = -1; + + setPacketListLoopMode(false, 0, 0); +} + +void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) +{ + currentPacketSequence_ = new PacketSequence; + currentPacketSequence_->repeatCount_ = repeats; + currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + + repeatDelayNsec/1000; + + repeatSequenceStart_ = packetSequenceList_.size(); + repeatSize_ = size; + packetCount_ = 0; + + packetSequenceList_.append(currentPacketSequence_); +} + +bool PcapPort::PortTransmitter::appendToPacketList(long sec, long nsec, + const uchar *packet, int length) +{ + bool op = true; + pcap_pkthdr pktHdr; + + pktHdr.caplen = pktHdr.len = length; + pktHdr.ts.tv_sec = sec; + pktHdr.ts.tv_usec = nsec/1000; + + if (currentPacketSequence_ == NULL || + !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) + { + if (currentPacketSequence_ != NULL) + { + long usecs; + + usecs = (pktHdr.ts.tv_sec + - currentPacketSequence_->lastPacket_->ts.tv_sec) + * long(1e6); + usecs += (pktHdr.ts.tv_usec + - currentPacketSequence_->lastPacket_->ts.tv_usec); + currentPacketSequence_->usecDelay_ = usecs; + } + + //! \todo (LOW): calculate sendqueue size + currentPacketSequence_ = new PacketSequence; + + packetSequenceList_.append(currentPacketSequence_); + + // Validate that the pkt will fit inside the new currentSendQueue_ + Q_ASSERT(currentPacketSequence_->hasFreeSpace( + sizeof(pcap_pkthdr) + length)); + } + + if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) + { + op = false; + } + + packetCount_++; + if (repeatSize_ > 0 && packetCount_ == repeatSize_) + { + qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", + repeatSequenceStart_, repeatSize_); + + // Set the packetSequence repeatSize + Q_ASSERT(repeatSequenceStart_ >= 0); + Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); + + if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) + { + PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; + + currentPacketSequence_->usecDelay_ = start->usecDelay_; + start->usecDelay_ = 0; + start->repeatSize_ = + packetSequenceList_.size() - repeatSequenceStart_; + } + + repeatSize_ = 0; + + // End current pktSeq and trigger a new pktSeq allocation for next pkt + currentPacketSequence_ = NULL; + } + + return op; +} + +void PcapPort::PortTransmitter::setHandle(pcap_t *handle) +{ + if (usingInternalHandle_) + pcap_close(handle_); + handle_ = handle; + usingInternalHandle_ = false; +} + +void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) +{ + if (usingInternalStats_) + delete stats_; + stats_ = stats; + usingInternalStats_ = false; +} + +void PcapPort::PortTransmitter::run() +{ + //! \todo (MED) Stream Mode - continuous: define before implement + + // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 + // 'coz of 2 reasons - there's no way of stopping it before all packets + // in the sendQueue are sent out and secondly, stats are available only + // when all packets have been sent - no periodic updates + // + // NOTE2: Transmit on the Rx Handle so that we can receive it back + // on the Tx Handle to do stats + // + // NOTE3: Update pcapExtra counters - port TxStats will be updated in the + // 'stats callback' function so that both Rx and Tx stats are updated + // together + + const int kSyncTransmit = 1; + int i; + long overHead = 0; // overHead should be negative or zero + + qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); + if (packetSequenceList_.size() <= 0) + goto _exit; + + for(i = 0; i < packetSequenceList_.size(); i++) { + qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, + packetSequenceList_.at(i)->repeatCount_, + packetSequenceList_.at(i)->repeatSize_, + packetSequenceList_.at(i)->usecDelay_); + qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, + packetSequenceList_.at(i)->packets_, + packetSequenceList_.at(i)->usecDuration_); + } + + state_ = kRunning; + i = 0; + while (i < packetSequenceList_.size()) + { + +_restart: + int rptSz = packetSequenceList_.at(i)->repeatSize_; + int rptCnt = packetSequenceList_.at(i)->repeatCount_; + + for (int j = 0; j < rptCnt; j++) + { + for (int k = 0; k < rptSz; k++) + { + int ret; + PacketSequence *seq = packetSequenceList_.at(i+k); +#ifdef Q_OS_WIN32 + TimeStamp ovrStart, ovrEnd; + + if (seq->usecDuration_ <= long(1e6)) // 1s + { + getTimeStamp(&ovrStart); + ret = pcap_sendqueue_transmit(handle_, + seq->sendQueue_, kSyncTransmit); + if (ret >= 0) + { + stats_->txPkts += seq->packets_; + stats_->txBytes += seq->bytes_; + + getTimeStamp(&ovrEnd); + overHead += seq->usecDuration_ + - udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + } + if (stop_) + ret = -2; + } + else + { + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); + } +#else + ret = sendQueueTransmit(handle_, seq->sendQueue_, + overHead, kSyncTransmit); +#endif + + if (ret >= 0) + { + long usecs = seq->usecDelay_ + overHead; + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + } + else + { + qDebug("error %d in sendQueueTransmit()", ret); + qDebug("overHead = %ld", overHead); + stop_ = false; + goto _exit; + } + } + } + + // Move to the next Packet Set + i += rptSz; + } + + if (returnToQIdx_ >= 0) + { + long usecs = loopDelay_ + overHead; + + if (usecs > 0) + { + udelay(usecs); + overHead = 0; + } + else + overHead = usecs; + + i = returnToQIdx_; + goto _restart; + } + +_exit: + state_ = kFinished; +} + +void PcapPort::PortTransmitter::start() +{ + // FIXME: return error + if (state_ == kRunning) { + qWarning("Transmit start requested but is already running!"); + return; + } + + state_ = kNotStarted; + QThread::start(); + + while (state_ == kNotStarted) + QThread::msleep(10); +} + +void PcapPort::PortTransmitter::stop() +{ + if (state_ == kRunning) { + stop_ = true; + while (state_ == kRunning) + QThread::msleep(10); + } + else { + // FIXME: return error + qWarning("Transmit stop requested but is not running!"); + return; + } +} + +bool PcapPort::PortTransmitter::isRunning() +{ + return (state_ == kRunning); +} + +int PcapPort::PortTransmitter::sendQueueTransmit(pcap_t *p, + pcap_send_queue *queue, long &overHead, int sync) +{ + TimeStamp ovrStart, ovrEnd; + struct timeval ts; + struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; + char *end = queue->buffer + queue->len; + + ts = hdr->ts; + + getTimeStamp(&ovrStart); + while((char*) hdr < end) + { + uchar *pkt = (uchar*)hdr + sizeof(*hdr); + int pktLen = hdr->caplen; + + if (sync) + { + long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + + (hdr->ts.tv_usec - ts.tv_usec); + + getTimeStamp(&ovrEnd); + + overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); + Q_ASSERT(overHead <= 0); + usec += overHead; + if (usec > 0) + { + udelay(usec); + overHead = 0; + } + else + overHead = usec; + + ts = hdr->ts; + getTimeStamp(&ovrStart); + } + + Q_ASSERT(pktLen > 0); + + pcap_sendpacket(p, pkt, pktLen); + stats_->txPkts++; + stats_->txBytes += pktLen; + + // Step to the next packet in the buffer + hdr = (struct pcap_pkthdr*) (pkt + pktLen); + pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); + + if (stop_) + { + return -2; + } + } + + return 0; +} + +void PcapPort::PortTransmitter::udelay(long usec) +{ +#if defined(Q_OS_WIN32) + LARGE_INTEGER tgtTicks; + LARGE_INTEGER curTicks; + + QueryPerformanceCounter(&curTicks); + tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/1000000; + + while (curTicks.QuadPart < tgtTicks.QuadPart) + QueryPerformanceCounter(&curTicks); +#elif defined(Q_OS_LINUX) + struct timeval delay, target, now; + + //qDebug("usec delay = %ld", usec); + + delay.tv_sec = 0; + delay.tv_usec = usec; + + while (delay.tv_usec >= 1000000) + { + delay.tv_sec++; + delay.tv_usec -= 1000000; + } + + gettimeofday(&now, NULL); + timeradd(&now, &delay, &target); + + do { + gettimeofday(&now, NULL); + } while (timercmp(&now, &target, <)); +#else + QThread::usleep(usec); +#endif +} + +PcapPort::PortCapturer::PortCapturer(const char *device) +{ + device_ = QString::fromAscii(device); + stop_ = false; + state_ = kNotStarted; + + if (!capFile_.open()) + qWarning("Unable to open temp cap file"); + + qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); + + dumpHandle_ = NULL; + handle_ = NULL; +} + +PcapPort::PortCapturer::~PortCapturer() +{ + capFile_.close(); +} + +void PcapPort::PortCapturer::run() +{ + int flag = PCAP_OPENFLAG_PROMISCUOUS; + char errbuf[PCAP_ERRBUF_SIZE] = ""; + + qDebug("In %s", __PRETTY_FUNCTION__); + + if (!capFile_.isOpen()) + { + qWarning("temp cap file is not open"); + goto _exit; + } +_retry: + handle_ = pcap_open_live(device_.toAscii().constData(), 65535, + flag, 1000 /* ms */, errbuf); + + if (handle_ == NULL) + { + if (flag && QString(errbuf).contains("promiscuous")) + { + qDebug("%s:can't set promiscuous mode, trying non-promisc", + device_.toAscii().constData()); + flag = 0; + goto _retry; + } + else + { + qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, + device_.toAscii().constData(), errbuf); + goto _exit; + } + } + + dumpHandle_ = pcap_dump_open(handle_, + capFile_.fileName().toAscii().constData()); + state_ = kRunning; + while (1) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle_, &hdr, &data); + switch (ret) + { + case 1: + pcap_dump((uchar*) dumpHandle_, hdr, data); + break; + case 0: + // timeout: just go back to the loop + break; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); + break; + case -2: + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + + if (stop_) + { + qDebug("user requested capture stop\n"); + break; + } + } + pcap_dump_close(dumpHandle_); + pcap_close(handle_); + dumpHandle_ = NULL; + handle_ = NULL; + stop_ = false; + +_exit: + state_ = kFinished; +} + +void PcapPort::PortCapturer::start() +{ + // FIXME: return error + if (state_ == kRunning) { + qWarning("Capture start requested but is already running!"); + return; + } + + state_ = kNotStarted; + QThread::start(); + + while (state_ == kNotStarted) + QThread::msleep(10); +} + +void PcapPort::PortCapturer::stop() +{ + if (state_ == kRunning) { + stop_ = true; + while (state_ == kRunning) + QThread::msleep(10); + } + else { + // FIXME: return error + qWarning("Capture stop requested but is not running!"); + return; + } +} + +bool PcapPort::PortCapturer::isRunning() +{ + return (state_ == kRunning); +} + +QFile* PcapPort::PortCapturer::captureFile() +{ + return &capFile_; +} diff --git a/server/pcapport.h b/server/pcapport.h new file mode 100644 index 0000000..b25ab5c --- /dev/null +++ b/server/pcapport.h @@ -0,0 +1,236 @@ +/* +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 +*/ + +#ifndef _SERVER_PCAP_PORT_H +#define _SERVER_PCAP_PORT_H + +#include +#include +#include + +#include "abstractport.h" +#include "pcapextra.h" + +class PcapPort : public AbstractPort +{ +public: + PcapPort(int id, const char *device); + ~PcapPort(); + + void init(); + + virtual bool hasExclusiveControl() { return false; } + virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } + + virtual void clearPacketList() { + transmitter_->clearPacketList(); + setPacketListLoopMode(false, 0, 0); + } + virtual void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec) { + transmitter_->loopNextPacketSet(size, repeats, + repeatDelaySec, repeatDelayNsec); + } + virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, + int length) { + return transmitter_->appendToPacketList(sec, nsec, packet, length); + } + virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) + { + transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); + } + + virtual void startTransmit() { + Q_ASSERT(!isDirty()); + transmitter_->start(); + } + virtual void stopTransmit() { transmitter_->stop(); } + virtual bool isTransmitOn() { return transmitter_->isRunning(); } + + virtual void startCapture() { capturer_->start(); } + virtual void stopCapture() { capturer_->stop(); } + virtual bool isCaptureOn() { return capturer_->isRunning(); } + virtual QIODevice* captureData() { return capturer_->captureFile(); } + +protected: + enum Direction + { + kDirectionRx, + kDirectionTx + }; + + class PortMonitor: public QThread + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + ~PortMonitor(); + void run(); + void stop(); + pcap_t* handle() { return handle_; } + Direction direction() { return direction_; } + bool isDirectional() { return isDirectional_; } + bool isPromiscuous() { return isPromisc_; } + protected: + AbstractPort::PortStats *stats_; + bool stop_; + private: + pcap_t *handle_; + Direction direction_; + bool isDirectional_; + bool isPromisc_; + }; + + class PortTransmitter: public QThread + { + public: + PortTransmitter(const char *device); + ~PortTransmitter(); + void clearPacketList(); + void loopNextPacketSet(qint64 size, qint64 repeats, + long repeatDelaySec, long repeatDelayNsec); + bool appendToPacketList(long sec, long usec, const uchar *packet, + int length); + void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { + returnToQIdx_ = loop ? 0 : -1; + loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; + } + void setHandle(pcap_t *handle); + void useExternalStats(AbstractPort::PortStats *stats); + void run(); + void start(); + void stop(); + bool isRunning(); + private: + enum State + { + kNotStarted, + kRunning, + kFinished + }; + + class PacketSequence + { + public: + PacketSequence() { + sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); + lastPacket_ = NULL; + packets_ = 0; + bytes_ = 0; + usecDuration_ = 0; + repeatCount_ = 1; + repeatSize_ = 1; + usecDelay_ = 0; + } + ~PacketSequence() { + pcap_sendqueue_destroy(sendQueue_); + } + bool hasFreeSpace(int size) { + if ((sendQueue_->len + size) <= sendQueue_->maxlen) + return true; + else + return false; + } + int appendPacket(const struct pcap_pkthdr *pktHeader, + const uchar *pktData) { + if (lastPacket_) + { + usecDuration_ += (pktHeader->ts.tv_sec + - lastPacket_->ts.tv_sec) * long(1e6); + usecDuration_ += (pktHeader->ts.tv_usec + - lastPacket_->ts.tv_usec); + } + packets_++; + bytes_ += pktHeader->caplen; + lastPacket_ = (struct pcap_pkthdr *) + (sendQueue_->buffer + sendQueue_->len); + return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); + } + pcap_send_queue *sendQueue_; + struct pcap_pkthdr *lastPacket_; + long packets_; + long bytes_; + ulong usecDuration_; + int repeatCount_; + int repeatSize_; + long usecDelay_; + }; + + void udelay(long usec); + int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, + int sync); + + quint64 ticksFreq_; + QList packetSequenceList_; + PacketSequence *currentPacketSequence_; + int repeatSequenceStart_; + quint64 repeatSize_; + quint64 packetCount_; + + int returnToQIdx_; + quint64 loopDelay_; + + bool usingInternalStats_; + AbstractPort::PortStats *stats_; + bool usingInternalHandle_; + pcap_t *handle_; + volatile bool stop_; + volatile State state_; + }; + + class PortCapturer: public QThread + { + public: + PortCapturer(const char *device); + ~PortCapturer(); + void run(); + void start(); + void stop(); + bool isRunning(); + QFile* captureFile(); + + private: + enum State + { + kNotStarted, + kRunning, + kFinished + }; + + QString device_; + volatile bool stop_; + QTemporaryFile capFile_; + pcap_t *handle_; + pcap_dumper_t *dumpHandle_; + volatile State state_; + }; + + PortMonitor *monitorRx_; + PortMonitor *monitorTx_; + + void updateNotes(); + +private: + PortTransmitter *transmitter_; + PortCapturer *capturer_; + + static pcap_if_t *deviceList_; +}; + +#endif diff --git a/server/portmanager.cpp b/server/portmanager.cpp new file mode 100644 index 0000000..897ad4f --- /dev/null +++ b/server/portmanager.cpp @@ -0,0 +1,144 @@ +/* +Copyright (C) 2010-2012 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 "portmanager.h" + +#include "bsdport.h" +#include "linuxport.h" +#include "pcapport.h" +#include "settings.h" +#include "winpcapport.h" + +#include +#include + +PortManager *PortManager::instance_ = NULL; + +PortManager::PortManager() +{ + int i; + pcap_if_t *deviceList; + pcap_if_t *device; + char errbuf[PCAP_ERRBUF_SIZE]; + + qDebug("Retrieving the device list from the local machine\n"); + + if (pcap_findalldevs(&deviceList, errbuf) == -1) + qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); + + for(device = deviceList, i = 0; device != NULL; device = device->next, i++) + { + AbstractPort *port; + + qDebug("%d. %s", i, device->name); + if (device->description) + qDebug(" (%s)\n", device->description); + +#if defined(Q_OS_WIN32) + if (!filterAcceptsPort(device->description)) +#else + if (!filterAcceptsPort(device->name)) +#endif + { + qDebug("%s (%s) rejected by filter. Skipping!", + device->name, device->description); + i--; + continue; + } + +#if defined(Q_OS_WIN32) + port = new WinPcapPort(i, device->name); +#elif defined(Q_OS_LINUX) + port = new LinuxPort(i, device->name); +#elif defined(Q_OS_BSD4) + port = new BsdPort(i, device->name); +#else + port = new PcapPort(i, device->name); +#endif + + if (!port->isUsable()) + { + qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, + device->name); + delete port; + i--; + continue; + } + + portList_.append(port); + } + + pcap_freealldevs(deviceList); + + foreach(AbstractPort *port, portList_) + port->init(); + + return; +} + +PortManager::~PortManager() +{ + while (!portList_.isEmpty()) + delete portList_.takeFirst(); +} + +PortManager* PortManager::instance() +{ + if (!instance_) + instance_ = new PortManager; + + return instance_; +} + +bool PortManager::filterAcceptsPort(const char *name) +{ + QRegExp pattern; + QStringList includeList = appSettings->value(kPortListIncludeKey) + .toStringList(); + QStringList excludeList = appSettings->value(kPortListExcludeKey) + .toStringList(); + + pattern.setPatternSyntax(QRegExp::Wildcard); + + // An empty (or missing) includeList accepts all ports + // NOTE: A blank "IncludeList=" is read as a stringlist with one + // string which is empty => treat it same as an empty stringlist + if (includeList.isEmpty() + || (includeList.size() == 1 && includeList.at(0).isEmpty())) + goto _include_pass; + + foreach (QString str, includeList) { + pattern.setPattern(str); + if (pattern.exactMatch(name)) + goto _include_pass; + } + + // IncludeList is not empty and port did not match a pattern + return false; + +_include_pass: + foreach (QString str, excludeList) { + pattern.setPattern(str); + if (pattern.exactMatch(name)) + return false; + } + + // Port did not match a pattern in ExcludeList + return true; +} diff --git a/server/portmanager.h b/server/portmanager.h new file mode 100644 index 0000000..801fbf1 --- /dev/null +++ b/server/portmanager.h @@ -0,0 +1,46 @@ +/* +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 +*/ + +#ifndef _SERVER_PORT_MANAGER_H +#define _SERVER_PORT_MANAGER_H + +#include +#include "abstractport.h" + +class PortManager +{ +public: + PortManager(); + ~PortManager(); + + int portCount() { return portList_.size(); } + AbstractPort* port(int id) { return portList_[id]; } + + static PortManager* instance(); + +private: + bool filterAcceptsPort(const char *name); + +private: + QList portList_; + + static PortManager *instance_; +}; + +#endif diff --git a/server/settings.h b/server/settings.h new file mode 100644 index 0000000..50a3b03 --- /dev/null +++ b/server/settings.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2014 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 +*/ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include +#include + +extern QSettings *appSettings; + +// +// PortList Section Keys +// +const QString kPortListIncludeKey("PortList/Include"); +const QString kPortListExcludeKey("PortList/Exclude"); + +#endif diff --git a/server/winpcapport.cpp b/server/winpcapport.cpp new file mode 100644 index 0000000..f42adaa --- /dev/null +++ b/server/winpcapport.cpp @@ -0,0 +1,226 @@ +/* +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 +*/ + +#include "winpcapport.h" + +#include +#include + +#ifdef Q_OS_WIN32 + +const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; + +WinPcapPort::WinPcapPort(int id, const char *device) + : PcapPort(id, device) +{ + monitorRx_->stop(); + monitorTx_->stop(); + monitorRx_->wait(); + monitorTx_->wait(); + + delete monitorRx_; + delete monitorTx_; + + monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); + monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); + + adapter_ = PacketOpenAdapter((CHAR*)device); + if (!adapter_) + qFatal("Unable to open adapter %s", device); + linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + + sizeof(uint)); + if (!linkStateOid_) + qFatal("failed to alloc oidData"); + + data_.set_is_exclusive_control(hasExclusiveControl()); + minPacketSetSize_ = 256; +} + +WinPcapPort::~WinPcapPort() +{ +} + +OstProto::LinkState WinPcapPort::linkState() +{ + memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); + + linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; + linkStateOid_->Length = sizeof(uint); + + if (PacketRequest(adapter_, 0, linkStateOid_)) + { + uint state; + + if (linkStateOid_->Length == sizeof(state)) + { + memcpy((void*)&state, (void*)linkStateOid_->Data, + linkStateOid_->Length); + if (state == 0) + linkState_ = OstProto::LinkStateUp; + else if (state == 1) + linkState_ = OstProto::LinkStateDown; + } + } + + return linkState_; +} + +bool WinPcapPort::hasExclusiveControl() +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + int exitCode; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + exitCode = QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName); + + qDebug("%s: exit code %d", __FUNCTION__, exitCode); + + if (exitCode == 0) + return true; + else + return false; +} + +bool WinPcapPort::setExclusiveControl(bool exclusive) +{ + QString portName(adapter_->Name + strlen("\\Device\\NPF_")); + QString bindConfigFilePath(QCoreApplication::applicationDirPath() + + "/bindconfig.exe"); + QString status; + + qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); + + if (!QFile::exists(bindConfigFilePath)) + return false; + + status = exclusive ? "disable" : "enable"; + + QProcess::execute(bindConfigFilePath, + QStringList() << "comp" << portName << status); + + updateNotes(); + + return (exclusive == hasExclusiveControl()); +} + +WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats) + : PcapPort::PortMonitor(device, direction, stats) +{ + if (handle()) + pcap_setmode(handle(), MODE_STAT); +} + +void WinPcapPort::PortMonitor::run() +{ + struct timeval lastTs; + quint64 lastTxPkts = 0; + quint64 lastTxBytes = 0; + + qDebug("in %s", __PRETTY_FUNCTION__); + + lastTs.tv_sec = 0; + lastTs.tv_usec = 0; + + while (!stop_) + { + int ret; + struct pcap_pkthdr *hdr; + const uchar *data; + + ret = pcap_next_ex(handle(), &hdr, &data); + switch (ret) + { + case 1: + { + quint64 pkts = *((quint64*)(data + 0)); + quint64 bytes = *((quint64*)(data + 8)); + + // TODO: is it 12 or 16? + bytes -= pkts * 12; + + uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + + (hdr->ts.tv_usec - lastTs.tv_usec); + + switch (direction()) + { + case kDirectionRx: + stats_->rxPkts += pkts; + stats_->rxBytes += bytes; + stats_->rxPps = (pkts * 1000000) / usec; + stats_->rxBps = (bytes * 1000000) / usec; + break; + + case kDirectionTx: + if (isDirectional()) + { + stats_->txPkts += pkts; + stats_->txBytes += bytes; + } + else + { + // Assuming stats_->txXXX are updated externally + quint64 txPkts = stats_->txPkts; + quint64 txBytes = stats_->txBytes; + + pkts = txPkts - lastTxPkts; + bytes = txBytes - lastTxBytes; + + lastTxPkts = txPkts; + lastTxBytes = txBytes; + } + stats_->txPps = (pkts * 1000000) / usec; + stats_->txBps = (bytes * 1000000) / usec; + break; + + default: + Q_ASSERT(false); + } + + break; + } + case 0: + //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); + continue; + case -1: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + case -2: + qWarning("%s: error reading packet (%d): %s", + __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); + break; + default: + qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); + } + lastTs.tv_sec = hdr->ts.tv_sec; + lastTs.tv_usec = hdr->ts.tv_usec; + if (!stop_) + QThread::msleep(1000); + } +} + +#endif diff --git a/server/winpcapport.h b/server/winpcapport.h new file mode 100644 index 0000000..5ab2f9b --- /dev/null +++ b/server/winpcapport.h @@ -0,0 +1,56 @@ +/* +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 +*/ + +#ifndef _SERVER_WIN_PCAP_PORT_H +#define _SERVER_WIN_PCAP_PORT_H + +#include + +#ifdef Q_OS_WIN32 + +#include "pcapport.h" + +#include + +class WinPcapPort : public PcapPort +{ +public: + WinPcapPort(int id, const char *device); + ~WinPcapPort(); + + virtual OstProto::LinkState linkState(); + virtual bool hasExclusiveControl(); + virtual bool setExclusiveControl(bool exclusive); + +protected: + class PortMonitor: public PcapPort::PortMonitor + { + public: + PortMonitor(const char *device, Direction direction, + AbstractPort::PortStats *stats); + void run(); + }; +private: + LPADAPTER adapter_; + PPACKET_OID_DATA linkStateOid_ ; +}; + +#endif + +#endif diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..d5e8af1 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,109 @@ + +#include "ostprotolib.h" +#include "pcapfileformat.h" +#include "protocol.pb.h" +#include "protocolmanager.h" +#include "settings.h" + +#include +#include +#include +#include + +extern ProtocolManager *OstProtocolManager; + +QSettings *appSettings; + +#if defined(Q_OS_WIN32) +QString kGzipPathDefaultValue; +QString kDiffPathDefaultValue; +QString kAwkPathDefaultValue; +#endif + +int usage(int /*argc*/, char* argv[]) +{ + printf("usage:\n"); + printf("%s \n", argv[0]); + printf("command -\n"); + printf(" importpcap\n"); + + return 255; +} + +int testImportPcap(int argc, char* argv[]) +{ + bool isOk; + QString error; + + if (argc != 3) + { + printf("usage:\n"); + printf("%s importpcap \n", argv[0]); + return 255; + } + + OstProto::StreamConfigList streams; + QString inFile(argv[2]); + + isOk = pcapFileFormat.openStreams(inFile, streams, error); + if (!error.isEmpty()) + { + printf("%s: %s\n", + inFile.toAscii().constData(), error.toAscii().constData()); + } + + if (!isOk) + return 1; + + return 0; +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + int exitCode = 0; + + // app init starts ... +#if defined(Q_OS_WIN32) + kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; + kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; + kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; +#endif + + app.setApplicationName("Ostinato"); + app.setOrganizationName("Ostinato"); + + OstProtocolManager = new ProtocolManager(); + + /* (Portable Mode) If we have a .ini file in the same directory as the + executable, we use that instead of the platform specific location + and format for the settings */ + QString portableIni = QCoreApplication::applicationDirPath() + + "/ostinato.ini"; + if (QFile::exists(portableIni)) + appSettings = new QSettings(portableIni, QSettings::IniFormat); + else + appSettings = new QSettings(); + + OstProtoLib::setExternalApplicationPaths( + appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), + appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), + appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), + appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); + + // ... app init finished + + // + // identify and run specified test + // + if (argc < 2) + exitCode = usage(argc, argv); + else if (strcmp(argv[1],"importpcap") == 0) + exitCode = testImportPcap(argc, argv); + else + exitCode = usage(argc, argv); + + delete appSettings; + return exitCode; +} + diff --git a/test/rpctest.py b/test/rpctest.py new file mode 100644 index 0000000..3ec8ec7 --- /dev/null +++ b/test/rpctest.py @@ -0,0 +1,554 @@ +#! /usr/bin/env python + +# standard modules +import logging +import os +import subprocess +import sys +import time + +sys.path.insert(1, '../binding') +from core import ost_pb, DroneProxy +from rpc import RpcError +from protocols.mac_pb2 import mac +from protocols.ip4_pb2 import ip4, Ip4 + +class Test: + pass + +class TestSuite: + def __init__(self): + self.results = [] + self.total = 0 + self.passed = 0 + self.completed = False + + def test_begin(self, name): + test = Test() + test.name = name + test.passed = False + self.running = test + print('-----------------------------------------------------------') + print('@@TEST: %s' % name) + print('-----------------------------------------------------------') + + def test_end(self, result): + if self.running: + self.running.passed = result + self.results.append(self.running) + self.total = self.total + 1 + if result: + self.passed = self.passed + 1 + self.running = None + print('@@RESULT: %s' % ('PASS' if result else 'FAIL')) + else: + raise Exception('Test end without a test begin') + + def report(self): + print('===========================================================') + print('TEST REPORT') + print('===========================================================') + for test in self.results: + print('%s: %d' % (test.name, test.passed)) + print('Passed: %d/%d' % (self.passed, self.total)) + print('Completed: %d' % (self.completed)) + + def complete(self): + self.completed = True + + def passed(self): + return passed == total and self.completed + +# initialize defaults +host_name = '127.0.0.1' +tx_port_number = -1 +rx_port_number = -1 +drone_version = ['0', '0', '0'] + +if sys.platform == 'win32': + tshark = r'C:\Program Files\Wireshark\tshark.exe' +else: + tshark = 'tshark' + + +# setup logging +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +print('') +print('This test uses the following topology -') +print('') +print(' +-------+ ') +print(' | |Tx--->----+') +print(' | Drone | |') +print(' | |Rx---<----+') +print(' +-------+ ') +print('') +print('A loopback port is used as both the Tx and Rx ports') +print('') + +suite = TestSuite() +drone = DroneProxy(host_name) + +try: + # ----------------------------------------------------------------- # + # TESTCASE: Verify any RPC before checkVersion() fails and the server + # closes the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('anyRpcBeforeCheckVersionFails') + drone.channel.connect(drone.host, drone.port) + try: + port_id_list = drone.getPortIdList() + except RpcError as e: + if ('compatibility check pending' in str(e)): + passed = True + else: + raise + finally: + drone.channel.disconnect() + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify DroneProxy.connect() fails for incompatible version + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('connectFailsForIncompatibleVersion') + try: + drone.proxy_version = '0.1.1' + drone.connect() + except RpcError as e: + if ('needs client version' in str(e)): + passed = True + drone_version = str(e).split()[-1].split('.') + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() fails for invalid client version format + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionFailsForInvalidClientVersion') + try: + drone.proxy_version = '0-1-1' + drone.connect() + except RpcError as e: + if ('invalid version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns incompatible if the 'major' + # part of the numbering format is + # different than the server's version and the server closes + # the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsIncompatForDifferentMajorVersion') + try: + drone.proxy_version = (str(int(drone_version[0])+1) + + '.' + drone_version[1]) + drone.connect() + except RpcError as e: + #FIXME: How to check for a closed connection? + if ('needs client version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns incompatible if the 'minor' + # part of the numbering format is + # different than the server's version and the server closes + # the connection + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsIncompatForDifferentMinorVersion') + try: + drone.proxy_version = (drone_version[0] + + '.' + str(int(drone_version[1])+1)) + drone.connect() + except RpcError as e: + #FIXME: How to check for a closed connection? + if ('needs client version' in str(e)): + passed = True + else: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify checkVersion() returns compatible if the 'revision' + # part of the numbering format is + # different than the server's version + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('checkVersionReturnsCompatForDifferentRevisionVersion') + try: + drone.proxy_version = (drone_version[0] + + '.' + drone_version[1] + + '.' + '999') + drone.connect() + passed = True + except RpcError as e: + raise + finally: + drone.proxy_version = None + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # Baseline Configuration for subsequent testcases + # ----------------------------------------------------------------- # + + # connect to drone + log.info('connecting to drone(%s:%d)' + % (drone.hostName(), drone.portNumber())) + drone.connect() + + # retreive port id list + log.info('retreiving port list') + port_id_list = drone.getPortIdList() + + # retreive port config list + log.info('retreiving port config for all ports') + port_config_list = drone.getPortConfig(port_id_list) + + if len(port_config_list.port) == 0: + log.warning('drone has no ports!') + sys.exit(1) + + # iterate port list to find a loopback port to use as the tx/rx port id + print('Port List') + print('---------') + for port in port_config_list.port: + print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) + # use a loopback port as default tx/rx port + if ('lo' in port.name or 'loopback' in port.description.lower()): + tx_port_number = port.port_id.id + rx_port_number = port.port_id.id + + if tx_port_number < 0 or rx_port_number < 0: + log.warning('loopback port not found') + sys.exit(1) + + print('Using port %d as tx/rx port(s)' % tx_port_number) + + tx_port = ost_pb.PortIdList() + tx_port.port_id.add().id = tx_port_number; + + rx_port = ost_pb.PortIdList() + rx_port.port_id.add().id = rx_port_number; + + # add a stream + stream_id = ost_pb.StreamIdList() + stream_id.port_id.CopyFrom(tx_port.port_id[0]) + stream_id.stream_id.add().id = 1 + log.info('adding tx_stream %d' % stream_id.stream_id[0].id) + drone.addStream(stream_id) + + # configure the stream + stream_cfg = ost_pb.StreamConfigList() + stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) + s = stream_cfg.stream.add() + s.stream_id.id = stream_id.stream_id[0].id + s.core.is_enabled = True + s.control.num_packets = 10 + + # setup stream protocols as mac:eth2:ip4:udp:payload + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber + p.Extensions[mac].dst_mac = 0x001122334455 + p.Extensions[mac].src_mac = 0x00aabbccddee + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber + + p = s.protocol.add() + p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber + # reduce typing by creating a shorter reference to p.Extensions[ip4] + ip = p.Extensions[ip4] + ip.src_ip = 0x01020304 + ip.dst_ip = 0x05060708 + ip.dst_ip_mode = Ip4.e_im_inc_host + + s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + + log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) + drone.modifyStream(stream_cfg) + + # clear tx/rx stats + log.info('clearing tx/rx stats') + drone.clearStats(tx_port) + drone.clearStats(rx_port) + + # ----------------------------------------------------------------- # + # TODO: + # TESTCASE: Verify a RPC with missing required fields in request fails + # and subsequently passes when the fields are initialized + # ----------------------------------------------------------------- # +# passed = False +# suite.test_begin('rpcWithMissingRequiredFieldsFails') +# pid = ost_pb.PortId() +# try: +# sid_list = drone.getStreamIdList(pid) +# except RpcError as e: +# if ('missing required fields in request' in str(e)): +# passed = True +# else: +# raise +# +# try: +# pid.id = tx_port_number +# sid_list = drone.getStreamIdList(pid) +# except RpcError as e: +# passed = False +# raise +# finally: +# suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking addStream() during transmit fails + # TESTCASE: Verify invoking modifyStream() during transmit fails + # TESTCASE: Verify invoking deleteStream() during transmit fails + # ----------------------------------------------------------------- # + sid = ost_pb.StreamIdList() + sid.port_id.CopyFrom(tx_port.port_id[0]) + sid.stream_id.add().id = 2 + + passed = False + suite.test_begin('addStreamDuringTransmitFails') + drone.startTransmit(tx_port) + try: + log.info('adding tx_stream %d' % sid.stream_id[0].id) + drone.addStream(sid) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + passed = False + suite.test_begin('modifyStreamDuringTransmitFails') + scfg = ost_pb.StreamConfigList() + scfg.port_id.CopyFrom(tx_port.port_id[0]) + s = scfg.stream.add() + s.stream_id.id = sid.stream_id[0].id + s.protocol.add().protocol_id.id = ost_pb.Protocol.kMacFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kArpFieldNumber + s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber + drone.startTransmit(tx_port) + try: + log.info('configuring tx_stream %d' % sid.stream_id[0].id) + drone.modifyStream(scfg) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + passed = False + suite.test_begin('deleteStreamDuringTransmitFails') + drone.startTransmit(tx_port) + try: + log.info('deleting tx_stream %d' % sid.stream_id[0].id) + drone.deleteStream(sid) + except RpcError as e: + if ('Port Busy' in str(e)): + passed = True + else: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking startTransmit() during transmit is a NOP, + # not a restart + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startTransmitDuringTransmitIsNopNotRestart') + drone.startCapture(rx_port) + drone.startTransmit(tx_port) + try: + log.info('sleeping for 4s ...') + time.sleep(4) + log.info('starting transmit multiple times') + drone.startTransmit(tx_port) + time.sleep(1) + drone.startTransmit(tx_port) + time.sleep(1) + drone.startTransmit(tx_port) + time.sleep(1) + log.info('waiting for transmit to finish ...') + time.sleep(5) + drone.stopTransmit(tx_port) + drone.stopCapture(rx_port) + + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking startCapture() during capture is a NOP, + # not a restart + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startCaptureDuringTransmitIsNopNotRestart') + try: + drone.startCapture(rx_port) + drone.startTransmit(tx_port) + log.info('sleeping for 4s ...') + time.sleep(4) + log.info('starting capture multiple times') + drone.startCapture(rx_port) + time.sleep(1) + drone.startCapture(rx_port) + time.sleep(1) + drone.startCapture(rx_port) + time.sleep(1) + log.info('waiting for transmit to finish ...') + time.sleep(5) + drone.stopTransmit(tx_port) + drone.stopCapture(rx_port) + + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking stopTransmit() when transmit is not running + # is a NOP + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('stopTransmitWhenTransmitNotRunningIsNop') + try: + tx_stats = drone.getStats(tx_port) + log.info('--> (tx_stats)' + tx_stats.__str__()) + if tx_stats.port_stats[0].state.is_transmit_on: + raise Exception('Unexpected transmit ON state') + log.info('stopping transmit multiple times') + drone.stopTransmit(tx_port) + time.sleep(1) + drone.stopTransmit(tx_port) + time.sleep(1) + drone.stopTransmit(tx_port) + + # if we reached here, that means there was no exception + passed = True + except RpcError as e: + raise + finally: + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify invoking stopCapture() when capture is not running + # is a NOP + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('stopCaptureWhenCaptureNotRunningIsNop') + try: + rx_stats = drone.getStats(rx_port) + log.info('--> (rx_stats)' + rx_stats.__str__()) + if rx_stats.port_stats[0].state.is_capture_on: + raise Exception('Unexpected capture ON state') + log.info('stopping capture multiple times') + drone.stopCapture(rx_port) + time.sleep(1) + drone.stopCapture(rx_port) + time.sleep(1) + drone.stopCapture(rx_port) + + # if we reached here, that means there was no exception + passed = True + except RpcError as e: + raise + finally: + suite.test_end(passed) + + # ----------------------------------------------------------------- # + # TESTCASE: Verify startCapture(), startTransmit() sequence captures the + # first packet + # TESTCASE: Verify stopTransmit(), stopCapture() sequence captures the + # last packet + # ----------------------------------------------------------------- # + passed = False + suite.test_begin('startStopTransmitCaptureOrderCapturesAllPackets') + try: + drone.startCapture(rx_port) + drone.startTransmit(tx_port) + log.info('waiting for transmit to finish ...') + time.sleep(12) + drone.stopTransmit(tx_port) + drone.stopCapture(rx_port) + + log.info('getting Rx capture buffer') + buff = drone.getCaptureBuffer(rx_port.port_id[0]) + drone.saveCaptureBuffer(buff, 'capture.pcap') + log.info('dumping Rx capture buffer') + cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) + print(cap_pkts) + if '5.6.7.8' in cap_pkts and '5.6.7.17' in cap_pkts: + passed = True + os.remove('capture.pcap') + except RpcError as e: + raise + finally: + drone.stopTransmit(tx_port) + suite.test_end(passed) + + suite.complete() + + # delete streams + log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) + drone.deleteStream(stream_id) + + # bye for now + drone.disconnect() + +except Exception as ex: + log.exception(ex) + +finally: + suite.report() + if not suite.passed: + sys.exit(2); diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..e8542e4 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,39 @@ +TEMPLATE = app +CONFIG += qt console +QT += xml network script +INCLUDEPATH += "../rpc/" "../common/" "../client" +win32 { + LIBS += -lwpcap -lpacket + CONFIG(debug, debug|release) { + LIBS += -L"../common/debug" -lostprotogui -lostproto + LIBS += -L"../rpc/debug" -lpbrpc + POST_TARGETDEPS += \ + "../common/debug/libostprotogui.a" \ + "../common/debug/libostproto.a" \ + "../rpc/debug/libpbrpc.a" + } else { + LIBS += -L"../common/release" -lostprotogui -lostproto + LIBS += -L"../rpc/release" -lpbrpc + POST_TARGETDEPS += \ + "../common/release/libostprotogui.a" \ + "../common/release/libostproto.a" \ + "../rpc/release/libpbrpc.a" + } +} else { + LIBS += -lpcap + LIBS += -L"../common" -lostprotogui -lostproto + LIBS += -L"../rpc" -lpbrpc + POST_TARGETDEPS += \ + "../common/libostprotogui.a" \ + "../common/libostproto.a" \ + "../rpc/libpbrpc.a" +} +LIBS += -lprotobuf +LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 + +HEADERS += +SOURCES += main.cpp + +QMAKE_DISTCLEAN += object_script.* + +include(../install.pri) diff --git a/version.pri b/version.pri new file mode 100644 index 0000000..97b3673 --- /dev/null +++ b/version.pri @@ -0,0 +1,42 @@ +APP_VERSION = 0.6 +APP_REVISION = $(shell hg identify -i) +#uncomment the below line in a source package and fill-in the correct revision +#APP_REVISION = @ + +ver_info { + APP_VERSION_FILE = version.cpp + revtarget.target = $$APP_VERSION_FILE + win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ + "const char *revision = \"$$APP_REVISION\";" \ + > $$APP_VERSION_FILE + unix:revtarget.commands = echo \ + "\"const char *version = \\\"$$APP_VERSION\\\";" \ + "const char *revision = \\\"$$APP_REVISION\\\";\"" \ + > $$APP_VERSION_FILE + revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS + + SOURCES += $$APP_VERSION_FILE + QMAKE_EXTRA_TARGETS += revtarget + POST_TARGETDEPS += $$APP_VERSION_FILE + QMAKE_DISTCLEAN += $$APP_VERSION_FILE +} + +pkg_info { + PKG_INFO_FILE = pkg_info.json + pkginfo.target = $$PKG_INFO_FILE + pkginfo.CONFIG = recursive + win32:pkginfo.commands = echo "{" \ + " \"version\": \"$$APP_VERSION\"," \ + " \"revision\": \"$$APP_REVISION\"" \ + "}" \ + > $$PKG_INFO_FILE + unix:pkginfo.commands = echo "\"{" \ + " \\\"version\\\": \\\"$$APP_VERSION\\\"," \ + " \\\"revision\\\": \\\"$$APP_REVISION\\\"" \ + "}\"" \ + > $$PKG_INFO_FILE + + QMAKE_EXTRA_TARGETS += pkginfo + POST_TARGETDEPS += $$PKG_INFO_FILE + QMAKE_DISTCLEAN += $$PKG_INFO_FILE +} From 10d921adfcd3763a5220660408aa723a2442bc47 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 1 Mar 2015 21:36:13 +0530 Subject: [PATCH 292/294] Export streams as a python script for use with python-ostinato --- client/portswindow.cpp | 3 +- common/abstractfileformat.cpp | 7 +- common/ostprotogui.pro | 2 + common/pythonfileformat.cpp | 553 ++++++++++++++++++++++++++++++++++ common/pythonfileformat.h | 59 ++++ 5 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 common/pythonfileformat.cpp create mode 100644 common/pythonfileformat.h diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 1f3bc4d..41004c9 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -636,7 +636,8 @@ _retry: goto _exit; fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); - if (!fileType.startsWith("Ostinato")) + if (!fileType.startsWith("Ostinato") + && !fileType.startsWith("Python")) { if (QMessageBox::warning(this, tr("Ostinato"), QString("You have chosen to save in %1 format. All stream " diff --git a/common/abstractfileformat.cpp b/common/abstractfileformat.cpp index 15271d7..234795a 100644 --- a/common/abstractfileformat.cpp +++ b/common/abstractfileformat.cpp @@ -22,6 +22,7 @@ along with this program. If not, see #include "fileformat.h" #include "pcapfileformat.h" #include "pdmlfileformat.h" +#include "pythonfileformat.h" #include @@ -49,7 +50,8 @@ QStringList AbstractFileFormat::supportedFileTypes() return QStringList() << "Ostinato (*)" << "PCAP (*)" - << "PDML (*.pdml)"; + << "PDML (*.pdml)" + << "PythonScript (*.py)"; } void AbstractFileFormat::openStreamsOffline(const QString fileName, @@ -110,6 +112,9 @@ AbstractFileFormat* AbstractFileFormat::fileFormatFromType( if (pcapFileFormat.isMyFileType(fileType)) return &pcapFileFormat; + if (pythonFileFormat.isMyFileType(fileType)) + return &pythonFileFormat; + return NULL; } diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index b075046..dcbec0c 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -40,6 +40,7 @@ HEADERS = \ ipv6addressdelegate.h \ pcapfileformat.h \ pdmlfileformat.h \ + pythonfileformat.h \ pdmlprotocol.h \ pdmlprotocols.h \ pdmlreader.h @@ -80,6 +81,7 @@ SOURCES += \ fileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ + pythonfileformat.cpp \ pdmlprotocol.cpp \ pdmlprotocols.cpp \ pdmlreader.cpp \ diff --git a/common/pythonfileformat.cpp b/common/pythonfileformat.cpp new file mode 100644 index 0000000..73f847f --- /dev/null +++ b/common/pythonfileformat.cpp @@ -0,0 +1,553 @@ +/* +Copyright (C) 2015 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 "pythonfileformat.h" + +#include + +#include +#include + +#include +#include + +using google::protobuf::Message; +using google::protobuf::Reflection; +using google::protobuf::FieldDescriptor; + +PythonFileFormat pythonFileFormat; + +extern char *version; +extern char *revision; + +PythonFileFormat::PythonFileFormat() +{ + // Nothing to do +} + +PythonFileFormat::~PythonFileFormat() +{ + // Nothing to do +} + +bool PythonFileFormat::openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error) +{ + // NOT SUPPORTED! + return false; +} + +bool PythonFileFormat::saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error) +{ + QFile file(fileName); + QTextStream out(&file); + QSet imports; + + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + // import standard modules + emit status("Writing imports ..."); + emit target(0); + writeStandardImports(out); + + emit target(streams.stream_size()); + // import protocols from respective modules + // build the import list using a QSet to eliminate duplicates + for (int i = 0; i < streams.stream_size(); i++) { + const OstProto::Stream &stream = streams.stream(i); + for (int j = 0 ; j < stream.protocol_size(); j++) { + const OstProto::Protocol &protocol = stream.protocol(j); + const Reflection *refl = protocol.GetReflection(); + std::vector fields; + + refl->ListFields(protocol, &fields); + for (uint k = 0; k < fields.size(); k++) { + // skip protocol_id field + if (fields.at(k)->number() == + OstProto::Protocol::kProtocolIdFieldNumber) + continue; + + if (fields.at(k)->file()->name() != + fields.at(k)->message_type()->file()->name()) { + imports.insert( + QString("%1 import %2").arg( + QString(fields.at(k)->message_type() + ->file()->name().c_str()) + .replace(".proto", "_pb2"), + fields.at(k)->message_type()->name().c_str())); + imports.insert( + QString("%1 import %2").arg( + QString(fields.at(k) + ->file()->name().c_str()) + .replace(".proto", "_pb2"), + fields.at(k)->name().c_str())); + } + else { + imports.insert( + QString("%1 import %2, %3").arg( + QString(fields.at(k)->file()->name().c_str()) + .replace(".proto", "_pb2"), + fields.at(k)->message_type()->name().c_str(), + fields.at(k)->name().c_str())); + } + } + } + emit progress(i); + } + // write the import statements + out << "# import ostinato modules\n"; + out << "from ostinato.core import DroneProxy, ost_pb\n"; + foreach (QString str, imports) + out << "from ostinato.protocols." << str << "\n"; + out << "\n"; + + // start of script - init, connect to drone etc. + emit status("Writing prologue ..."); + emit target(0); + writePrologue(out); + + // Add streams + emit status("Writing stream adds ..."); + emit target(streams.stream_size()); + out << " # ------------#\n"; + out << " # add streams #\n"; + out << " # ------------#\n"; + out << " stream_id = ost_pb.StreamIdList()\n"; + out << " stream_id.port_id.id = tx_port_number\n"; + for (int i = 0; i < streams.stream_size(); i++) { + out << " stream_id.stream_id.add().id = " + << streams.stream(i).stream_id().id() << "\n"; + emit progress(i); + } + out << " drone.addStream(stream_id)\n"; + out << "\n"; + + // Configure streams with actual values + emit status("Writing stream configuration ..."); + emit target(streams.stream_size()); + out << " # ------------------#\n"; + out << " # configure streams #\n"; + out << " # ------------------#\n"; + out << " stream_cfg = ost_pb.StreamConfigList()\n"; + out << " stream_cfg.port_id.id = tx_port_number\n"; + for (int i = 0; i < streams.stream_size(); i++) { + const OstProto::Stream &stream = streams.stream(i); + const Reflection *refl; + std::vector fields; + + out << "\n"; + out << " # stream " << stream.stream_id().id() << " " + << stream.core().name().c_str() << "\n"; + out << " s = stream_cfg.stream.add()\n"; + out << " s.stream_id.id = " + << stream.stream_id().id() << "\n"; + + // Stream Core values + refl = stream.core().GetReflection(); + refl->ListFields(stream.core(), &fields); + for (uint j = 0; j < fields.size(); j++) { + writeFieldAssignment(out, QString(" s.core.") + .append(fields.at(j)->name().c_str()), + stream.core(), refl, fields.at(j)); + } + + // Stream Control values + refl = stream.control().GetReflection(); + refl->ListFields(stream.control(), &fields); + for (uint j = 0; j < fields.size(); j++) { + writeFieldAssignment(out, QString(" s.control.") + .append(fields.at(j)->name().c_str()), + stream.control(), refl, fields.at(j)); + } + + // Protocols + for (int j = 0 ; j < stream.protocol_size(); j++) { + const OstProto::Protocol &protocol = stream.protocol(j); + + out << "\n" + << " p = s.protocol.add()\n" + << " p.protocol_id.id = " + << QString(OstProto::Protocol_k_descriptor() + ->FindValueByNumber(protocol.protocol_id().id()) + ->full_name().c_str()) + .replace("OstProto", "ost_pb"); + out << "\n"; + refl = protocol.GetReflection(); + refl->ListFields(protocol, &fields); + + for (uint k = 0; k < fields.size(); k++) { + // skip protocol_id field + if (fields.at(k)->number() == + OstProto::Protocol::kProtocolIdFieldNumber) + continue; + QString pfx(" p.Extensions[X]"); + pfx.replace("X", fields.at(k)->name().c_str()); + writeFieldAssignment(out, pfx, protocol, + refl, fields.at(k)); + } + } + emit progress(i); + } + out << "\n"; + out << " drone.modifyStream(stream_cfg)\n"; + + // end of script - transmit streams, disconnect from drone etc. + emit status("Writing epilogue ..."); + emit target(0); + writeEpilogue(out); + + out.flush(); + file.close(); + return true; + +_open_fail: + return false; +} + +bool PythonFileFormat::isMyFileFormat(const QString fileName) +{ + // isMyFileFormat() is used for file open case to detect + // file format - Open not supported for Python Scripts + return false; +} + +bool PythonFileFormat::isMyFileType(const QString fileType) +{ + if (fileType.startsWith("PythonScript")) + return true; + else + return false; +} + +// +// Private Member Functions +// +void PythonFileFormat::writeStandardImports(QTextStream &out) +{ + out << "#! /usr/bin/env python\n"; + out << "\n"; + out << "# This script was programmatically generated\n" + << "# by Ostinato version " << version + << " revision " << revision << "\n" + << "# The script should work out of the box mostly,\n" + << "# but occassionally might need minor tweaking\n" + << "# Please report any bugs at http://ostinato.org\n"; + out << "\n"; + out << "# standard modules\n"; + out << "import logging\n"; + out << "import os\n"; + out << "import sys\n"; + out << "import time\n"; + out << "\n"; +} + +void PythonFileFormat::writePrologue(QTextStream &out) +{ + out << "# initialize the below variables appropriately " + << "to avoid manual input\n"; + out << "host_name = ''\n"; + out << "tx_port_number = -1\n"; + out << "\n"; + out << "# setup logging\n"; + out << "log = logging.getLogger(__name__)\n"; + out << "logging.basicConfig(level=logging.INFO)\n"; + out << "\n"; + out << "# get inputs, if required\n"; + out << "while len(host_name) == 0:\n"; + out << " host_name = raw_input('Drone\\'s Hostname/IP: ')\n"; + out << "while tx_port_number < 0:\n"; + out << " tx_port_number = int(raw_input('Tx Port Number: '))\n"; + out << "\n"; + out << "drone = DroneProxy(host_name)\n"; + out << "\n"; + out << "try:\n"; + out << " # connect to drone\n"; + out << " log.info('connecting to drone(%s:%d)' \n"; + out << " % (drone.hostName(), drone.portNumber()))\n"; + out << " drone.connect()\n"; + out << "\n"; + out << " # setup tx port list\n"; + out << " tx_port = ost_pb.PortIdList()\n"; + out << " tx_port.port_id.add().id = tx_port_number;\n"; + out << "\n"; +} + +void PythonFileFormat::writeEpilogue(QTextStream &out) +{ + out << " # clear tx/rx stats\n"; + out << " log.info('clearing tx stats')\n"; + out << " drone.clearStats(tx_port)\n"; + out << "\n"; + out << " log.info('starting transmit')\n"; + out << " drone.startTransmit(tx_port)\n"; + out << "\n"; + out << " # wait for transmit to finish\n"; + out << " log.info('waiting for transmit to finish ...')\n"; + out << " while True:\n"; + out << " time.sleep(5)\n"; + out << " tx_stats = drone.getStats(tx_port)\n"; + out << " if tx_stats.port_stats[0].state.is_transmit_on == False:\n"; + out << " break\n"; + out << "\n"; + out << " # stop transmit and capture\n"; + out << " log.info('stopping transmit')\n"; + out << " drone.stopTransmit(tx_port)\n"; + out << "\n"; + out << " # get tx stats\n"; + out << " log.info('retreiving stats')\n"; + out << " tx_stats = drone.getStats(tx_port)\n"; + out << "\n"; + out << " log.info('tx pkts = %d' % (tx_stats.port_stats[0].tx_pkts))\n"; + out << "\n"; + out << " # delete streams\n"; + out << " log.info('deleting tx_streams')\n"; + out << " drone.deleteStream(stream_id)\n"; + out << "\n"; + out << " # bye for now\n"; + out << " drone.disconnect()\n"; + out << "\n"; + out << "except Exception as ex:\n"; + out << " log.exception(ex)\n"; + out << " sys.exit(1)\n"; +} + +void PythonFileFormat::writeFieldAssignment( + QTextStream &out, + QString fieldName, + const Message &msg, + const Reflection *refl, + const FieldDescriptor *fieldDesc, + int index) +{ + // for a repeated field, + // if index < 0 => we are writing a repeated aggregate + // if index >= 0 => we are writing a repeated element + if (fieldDesc->is_repeated() && (index < 0)) { + int n = refl->FieldSize(msg, fieldDesc); + QString var = singularize(fieldDesc->name().c_str()); + for (int i = 0; i < n; i++) { + out << " " << var << " = " << fieldName.trimmed() << ".add()\n"; + writeFieldAssignment(out, QString(" ").append(var), + msg, refl, fieldDesc, i); + } + return; + } + + // Ideally fields should not be set if they have the same + // value as the default value - but currently protocols don't + // check this when setting values in the protobuf data object + // so here we check that explicitly for each field and if true + // we don't output anything + switch(fieldDesc->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + { + qint32 val = fieldDesc->is_repeated() ? + refl->GetRepeatedInt32(msg, fieldDesc, index) : + refl->GetInt32(msg, fieldDesc); + if (val != fieldDesc->default_value_int32()) + out << fieldName << " = " << val << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_INT64: + { + qint64 val = fieldDesc->is_repeated() ? + refl->GetRepeatedInt64(msg, fieldDesc, index) : + refl->GetInt64(msg, fieldDesc); + if (val != fieldDesc->default_value_int64()) + out << fieldName << " = " << val << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_UINT32: + { + quint32 val = fieldDesc->is_repeated() ? + refl->GetRepeatedUInt32(msg, fieldDesc, index) : + refl->GetUInt32(msg, fieldDesc); + QString valStr; + + if (useDecimalBase(fieldName)) + valStr.setNum(val); + else + valStr.setNum(val, 16).prepend("0x"); + + if (val != fieldDesc->default_value_uint32()) + out << fieldName << " = " << valStr << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_UINT64: + { + quint64 val = fieldDesc->is_repeated() ? + refl->GetRepeatedUInt64(msg, fieldDesc, index) : + refl->GetUInt64(msg, fieldDesc); + QString valStr; + + if (useDecimalBase(fieldName)) + valStr.setNum(val); + else + valStr.setNum(val, 16).prepend("0x"); + + if (val != fieldDesc->default_value_uint64()) + out << fieldName << " = " << valStr << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_DOUBLE: + { + double val = fieldDesc->is_repeated() ? + refl->GetRepeatedDouble(msg, fieldDesc, index) : + refl->GetDouble(msg, fieldDesc); + if (val != fieldDesc->default_value_double()) + out << fieldName << " = " << val << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_FLOAT: + { + float val = fieldDesc->is_repeated() ? + refl->GetRepeatedFloat(msg, fieldDesc, index) : + refl->GetFloat(msg, fieldDesc); + if (val != fieldDesc->default_value_float()) + out << fieldName << " = " << val << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_BOOL: + { + bool val = fieldDesc->is_repeated() ? + refl->GetRepeatedBool(msg, fieldDesc, index) : + refl->GetBool(msg, fieldDesc); + if (val != fieldDesc->default_value_bool()) + out << fieldName + << " = " + << (refl->GetBool(msg, fieldDesc) ? "True" : "False") + << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_STRING: + { + std::string val = fieldDesc->is_repeated() ? + refl->GetRepeatedStringReference(msg, fieldDesc, index, &val) : + refl->GetStringReference(msg, fieldDesc, &val); + QString escVal = escapeString(val.c_str()); + if (val != fieldDesc->default_value_string()) + out << fieldName << " = '" << escVal << "'\n"; + break; + } + case FieldDescriptor::CPPTYPE_ENUM: + { + // Fields defined in protocol.proto are within ost_pb scope + QString module = fieldDesc->file()->name() == "protocol.proto" ? + "ost_pb." : ""; + std::string val = fieldDesc->is_repeated() ? + refl->GetRepeatedEnum(msg, fieldDesc, index)->full_name() : + refl->GetEnum(msg, fieldDesc)->full_name(); + if (val != fieldDesc->default_value_enum()->full_name()) + out << fieldName << " = " << QString::fromStdString(val) + .replace("OstProto.", module) + << "\n"; + break; + } + case FieldDescriptor::CPPTYPE_MESSAGE: + { + QString pfxStr(fieldName); + const Message &msg2 = fieldDesc->is_repeated() ? + refl->GetRepeatedMessage(msg, fieldDesc, index) : + refl->GetMessage(msg, fieldDesc); + const Reflection *refl2 = msg2.GetReflection(); + std::vector fields2; + QList autoFields; + + refl2->ListFields(msg2, &fields2); + + // Unfortunately, auto-calculated fields such as cksum, length + // and protocol-type etc. may be set in the protobuf even if + // they are not being overridden; + // Intelligence regarding them is inside the respective protocol + // implementation, not inside the protobuf objects - the latter + // is all we have available here to work with; + // We attempt a crude hack here to detect such fields and avoid + // writing assignment statements for them + for (uint i = 0; i < fields2.size(); i++) { + std::string name = fields2.at(i)->name(); + if ((fields2.at(i)->cpp_type() + == FieldDescriptor::CPPTYPE_BOOL) + && (name.find("is_override_") == 0) + && (refl2->GetBool(msg2, fields2.at(i)) == false)) { + name.erase(0, sizeof("is_override_") - 1); + autoFields.append(name); + } + } + + for (uint i = 0 ; i < fields2.size(); i++) { + // skip auto fields that are not overridden + if (autoFields.contains(fields2.at(i)->name())) + continue; + + writeFieldAssignment(out, + QString("%1.%2").arg(pfxStr, + fields2.at(i)->name().c_str()), + msg2, refl2, fields2.at(i)); + } + break; + } + default: + qWarning("unable to write field of unsupported type %d", + fieldDesc->cpp_type()); + } +} + +QString PythonFileFormat::singularize(QString plural) +{ + QString singular = plural; + + // Apply some heuristics + if (plural.endsWith("ies")) + singular.replace(singular.length()-3, 3, "y"); + else if (plural.endsWith("ses")) + singular.chop(2); + else if (plural.endsWith("s")) + singular.chop(1); + + return singular; +} + +QString PythonFileFormat::escapeString(QString str) +{ + QString escStr = ""; + for (int i=0; i < str.length(); i++) { + uchar c = str[i].cell(); + if ((c < 128) && isprint(c)) { + if (c == '\'') + escStr.append("\\'"); + else + escStr.append(str[i]); + } + else + escStr.append(QString("\\x%1").arg(int(c), 2, 16, QChar('0'))); + } + return escStr; +} + +bool PythonFileFormat::useDecimalBase(QString fieldName) +{ + // Heuristic - use Hex base for all except for the following + return fieldName.endsWith("count") + || fieldName.endsWith("length") + || fieldName.endsWith("len") + || fieldName.endsWith("time"); +} + diff --git a/common/pythonfileformat.h b/common/pythonfileformat.h new file mode 100644 index 0000000..55a6452 --- /dev/null +++ b/common/pythonfileformat.h @@ -0,0 +1,59 @@ +/* +Copyright (C) 2015 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 +*/ + +#ifndef _PYTHON_FILE_FORMAT_H +#define _PYTHON_FILE_FORMAT_H + +#include "abstractfileformat.h" + +#include + +class PythonFileFormat : public AbstractFileFormat +{ +public: + PythonFileFormat(); + ~PythonFileFormat(); + + virtual bool openStreams(const QString fileName, + OstProto::StreamConfigList &streams, QString &error); + virtual bool saveStreams(const OstProto::StreamConfigList streams, + const QString fileName, QString &error); + + bool isMyFileFormat(const QString fileName); + bool isMyFileType(const QString fileType); + +private: + void writeStandardImports(QTextStream &out); + void writePrologue(QTextStream &out); + void writeEpilogue(QTextStream &out); + void writeFieldAssignment(QTextStream &out, + QString fieldName, + const google::protobuf::Message &msg, + const google::protobuf::Reflection *refl, + const google::protobuf::FieldDescriptor *fieldDesc, + int index = -1); + QString singularize(QString plural); + QString escapeString(QString str); + bool useDecimalBase(QString fieldName); +}; + +extern PythonFileFormat pythonFileFormat; + +#endif + From ecbb2579123d197d73d50c07137acbc14d4b4720 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Sun, 1 Mar 2015 21:58:40 +0530 Subject: [PATCH 293/294] Add feature to duplicate selected streams to create user defined number of copies Fixes issue 23 --- client/icons/stream_duplicate.png | Bin 0 -> 859 bytes client/ostinato.qrc | 1 + client/port.cpp | 25 +++++++++++++++++++++++++ client/port.h | 2 ++ client/portswindow.cpp | 29 ++++++++++++++++++++++++++++- client/portswindow.h | 1 + client/portswindow.ui | 8 ++++++++ 7 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 client/icons/stream_duplicate.png diff --git a/client/icons/stream_duplicate.png b/client/icons/stream_duplicate.png new file mode 100644 index 0000000000000000000000000000000000000000..3262767cda95a217ac8d3fabf6c4bfe3514fd770 GIT binary patch literal 859 zcmV-h1El-)HU~Z@BQ7|ITsWqFPt%czwZJk^u%JZL0Ogu z7-JwwQn0tTclx3}?kvID+L{XidsxQ^&e&|W7P?hu`JbU6)Keq zD2hUaNxZkB72a$%4^2)^`UtC|A7te-nGAX1Xrjep5qO>_@7ffX%SGn`-ED7gLpU5( zk(@u5K{Og2Z)$29rKuELp-_NyIt_Job>MI~K&R7bgynLX>`sh~jA#lt3}_YQwqglZ ztJPR4mEiF3kO&v?|I7ONdO%xaZnyg`4MVH2u&{85WF^F8xkx0!oK7d7&*!07ENT-s zHZ~xYO7;1CzImGB_xm6GXq~MiVV0C|gzZGQ)-QC?rN*&h*fy7fxUu2>pgCsM)-Q?tMb$Vds z*E@*sEW?$R-d!Zlo`yI#H#gqa);3B?D6pBWXVBO67?`R6Qy3_qLZ+|_MxhlJx8`9r z{Xs@mdTouNQ0N7+J#TJqhNGh+O#w+J@OC~45~`3D2_z=L-&zrFU%dy%Qdzg0ic~cM z%s{~na19L&^isj*=4PpCtqL-e!E)J#V5X7%DWt(#}Pq3W$oGSy^Pc4j%jrk7_ z4xV5*S(C}slXQfB*Dx$m5ut)=uA6Vdoof#vmX1Pr{o_H2ueAT3P;2Ktrs3gX7h2gv zE62G1jK||?p|odbXLH|f%y4eoeRKHd`|tRw^&nXM?`u5!c)i}iTrM|2E9N*Z__gcJ lE2dmBR}@y4olxbIzJJd{=EC=vF*^VN002ovPDHLkV1lGDi4On( literal 0 HcmV?d00001 diff --git a/client/ostinato.qrc b/client/ostinato.qrc index 6162ac0..d2e81f2 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -33,6 +33,7 @@ icons/sound_none.png icons/stream_add.png icons/stream_delete.png + icons/stream_duplicate.png icons/stream_edit.png diff --git a/client/port.cpp b/client/port.cpp index e9e5915..e23f0ac 100644 --- a/client/port.cpp +++ b/client/port.cpp @@ -422,6 +422,31 @@ void Port::updateStats(OstProto::PortStats *portStats) } } +void Port::duplicateStreams(const QList &list, int count) +{ + QList sources; + foreach(int index, list) { + OstProto::Stream stream; + Q_ASSERT(index < mStreams.size()); + mStreams.at(index)->protoDataCopyInto(stream); + sources.append(stream); + } + + int insertAt = mStreams.size(); + for (int i=0; i < count; i++) { + for (int j=0; j < sources.size(); j++) { + newStreamAt(insertAt, &sources.at(j)); + // Rename stream by appending the copy count + mStreams.at(insertAt)->setName(QString("%1 (%2)") + .arg(mStreams.at(insertAt)->name()) + .arg(i+1)); + insertAt++; + } + } + + emit streamListChanged(mPortGroupId, mPortId); +} + bool Port::openStreams(QString fileName, bool append, QString &error) { bool ret = false; diff --git a/client/port.h b/client/port.h index 6df7a98..a0a249d 100644 --- a/client/port.h +++ b/client/port.h @@ -138,6 +138,8 @@ public: void recalculateAverageRates(); void updateStats(OstProto::PortStats *portStats); + void duplicateStreams(const QList &list, int count); + bool openStreams(QString fileName, bool append, QString &error); bool saveStreams(QString fileName, QString fileType, QString &error); diff --git a/client/portswindow.cpp b/client/portswindow.cpp index 41004c9..9fbbe67 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -60,6 +60,7 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) // Populate StramList Context Menu Actions tvStreamList->addAction(actionNew_Stream); tvStreamList->addAction(actionEdit_Stream); + tvStreamList->addAction(actionDuplicate_Stream); tvStreamList->addAction(actionDelete_Stream); sep = new QAction(this); @@ -280,7 +281,8 @@ void PortsWindow::updateStreamViewActions() actionEdit_Stream->setEnabled(true); } - // Delete is always enabled as long as we have a selection + // Duplicate/Delete are always enabled as long as we have a selection + actionDuplicate_Stream->setEnabled(true); actionDelete_Stream->setEnabled(true); } else @@ -291,6 +293,7 @@ void PortsWindow::updateStreamViewActions() else actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); + actionDuplicate_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } actionOpen_Streams->setEnabled(plm->isPort( @@ -525,6 +528,30 @@ void PortsWindow::on_actionEdit_Stream_triggered() } } +void PortsWindow::on_actionDuplicate_Stream_triggered() +{ + QItemSelectionModel* model = tvStreamList->selectionModel(); + QModelIndex current = tvPortList->selectionModel()->currentIndex(); + qDebug("Duplicate Stream Action"); + + if (model->hasSelection()) + { + bool isOk; + int count = QInputDialog::getInteger(this, "Duplicate Streams", + "Count", 1, 1, 9999, 1, &isOk); + + if (!isOk) + return; + + QList list; + foreach(QModelIndex index, model->selectedRows()) + list.append(index.row()); + plm->port(current).duplicateStreams(list, count); + } + else + qDebug("No selection"); +} + void PortsWindow::on_actionDelete_Stream_triggered() { qDebug("Delete Stream Action"); diff --git a/client/portswindow.h b/client/portswindow.h index 5c071f0..98338e9 100644 --- a/client/portswindow.h +++ b/client/portswindow.h @@ -74,6 +74,7 @@ private slots: void on_actionNew_Stream_triggered(); void on_actionEdit_Stream_triggered(); + void on_actionDuplicate_Stream_triggered(); void on_actionDelete_Stream_triggered(); void on_actionOpen_Streams_triggered(); diff --git a/client/portswindow.ui b/client/portswindow.ui index a5d9261..d3a2c55 100644 --- a/client/portswindow.ui +++ b/client/portswindow.ui @@ -258,6 +258,14 @@ Port Configuration ... + + + :/icons/stream_duplicate.png + + + Duplicate Stream + + From 0c861201b3aaa073e4e42b036ab41d1f5f5817d1 Mon Sep 17 00:00:00 2001 From: "Srivats P." Date: Thu, 9 Apr 2015 21:44:54 +0530 Subject: [PATCH 294/294] Save streams as Python Script: The generated script now checks for Ctrl-C while waiting for transmit to stop, so that cleanup can be done --- common/pythonfileformat.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/pythonfileformat.cpp b/common/pythonfileformat.cpp index 73f847f..a4aacfb 100644 --- a/common/pythonfileformat.cpp +++ b/common/pythonfileformat.cpp @@ -302,9 +302,14 @@ void PythonFileFormat::writeEpilogue(QTextStream &out) out << " # wait for transmit to finish\n"; out << " log.info('waiting for transmit to finish ...')\n"; out << " while True:\n"; - out << " time.sleep(5)\n"; - out << " tx_stats = drone.getStats(tx_port)\n"; - out << " if tx_stats.port_stats[0].state.is_transmit_on == False:\n"; + out << " try:\n"; + out << " time.sleep(5)\n"; + out << " tx_stats = drone.getStats(tx_port)\n"; + out << " if tx_stats.port_stats[0].state.is_transmit_on" + " == False:\n"; + out << " break\n"; + out << " except KeyboardInterrupt:\n"; + out << " log.info('transmit interrupted by user')\n"; out << " break\n"; out << "\n"; out << " # stop transmit and capture\n";